Skip to content

Implement custom feed favicons#7646

Merged
Alkarex merged 64 commits intoFreshRSS:edgefrom
Inverle:favicon-upload
Jun 30, 2025
Merged

Implement custom feed favicons#7646
Alkarex merged 64 commits intoFreshRSS:edgefrom
Inverle:favicon-upload

Conversation

@Inverle
Copy link
Member

@Inverle Inverle commented Jun 3, 2025

Closes #3789, #6503

Icon setting when no custom icon is set yet:

image

  • Change... button opens a file dialog, and after selecting a file shows the chosen icon in the preview on the left. Submit must be clicked after selecting the icon.
  • Reset to default changes the preview icon to the default one, and also requires Submit to be clicked to apply the changes.

Full list of changes:

  • CSP now includes blob: in img-src for
    • indexAction() and feedAction() in subscriptionController.php
    • all of the view actions in indexController.php
  • Introduce new attribute customFavicon (boolean) for feeds that indicates if the feed has a custom favicon
    • hashFavicon() in Feed.php is dependent on this attribute
      • hashFavicon() has a new parameter called skipCache (boolean) that allows the reset of the favicon hash for the Feed object
      • resetFaviconHash() just calls hashFavicon(skipCache: true)
  • f.php URLs now have the format of /f.php?h=XXXXX&t=cachebuster, where the t parameter is only used for serving custom favicons
    • if t parameter is set, f.php returns a Cache-Control: immutable header
  • stripos and strpos were changed to str_contains in various places (refactor)
  • JS for handling the custom favicon configuration logic is in extra.js inside init_update_feed() which is called when feed configuration is opened from the aside or when the subscription management page with the feed is loaded
    • Server-side code for uploading the icon in subscriptionController.php under feedAction()
    • Errors that may occur during the setting of a custom favicon:
      • Unsupported image file type (handled only server-side with isImgMime())
      • When the file is bigger than 1 MiB (default), handled both client-side and server-side
      • Standard feed error when updateFeed() fails
  • JS vars javascript_vars.phtml are no longer escaped with htmlspecialchars(), instead with json encoding,
  • CSS for disabled buttons was added
  • Max favicon file size is configurable with the max_favicon_upload_size option in config.php (not exposed via UI)
  • Custom favicons are currently deleted only when they are either reset to the default icon, or the feed gets deleted. They do not get deleted when the user deletes their account without removing their feeds first.
  • faviconPrepare() and faviconRebuild() are not allowed to be called when the customFavicon attribute is true
  • New i18n strings:
    • 'sub.feed.icon' => 'Icon'
    • 'sub.feed.change_favicon' => 'Change…'
    • 'sub.feed.reset_favicon' => 'Reset to default'
    • 'sub.feed.favicon_changed_by_ext' => 'The icon has been set by the <b>%s</b> extension.'
    • 'feedback.sub.feed.favicon.too_large' => 'Uploaded icon is too large. The maximum file size is <em>%s</em>.'
    • 'feedback.sub.feed.favicon.unsupported_format' => 'Unsupported image file format!'
  • Extension hook custom_favicon_hash
  • Extension hook custom_favicon_btn_url
    • Allows extensions to implement a button for setting a custom favicon for individual feeds by providing an URL. The URL will be sent a POST request with the extAction field set to either query_icon_info or update_icon, along with an id field which describes the feed's ID.

@Inverle
Copy link
Member Author

Inverle commented Jun 3, 2025

todo:

  • test on postgresql, mysql and sqlite: works on all of them
  • check if the new customFavicon column gets added after applying the PR to an existing installation: works on postgresql, mysql and sqlite <--- PR is no longer using a new column (outdated TODO entry)
  • make custom favicon display correctly on shared HTML query page: breaks when bookmarking just one feed, and is not related to this PR ([Bug] Favicon doesn't display in user query with a single feed #7652)
  • use a view for the new action so that there is no [notice] --- File not found: /views/javascript/originalIconUrl.phtml message
  • consider better styling for disabled button

@Alkarex Alkarex added this to the 1.27.0 milestone Jun 3, 2025
@Alkarex
Copy link
Member

Alkarex commented Jun 4, 2025

I am wondering what is most demanded between uploading a favicon and providing an URL.
Maybe you could ask for feedback in e.g., #3789, #6503

@Inverle
Copy link
Member Author

Inverle commented Jun 4, 2025

I am wondering what is most demanded between uploading a favicon and providing an URL.

We could have both (though what would be the use case for providing an URL instead of just downloading the desired favicon and uploading it?)

@Alkarex
Copy link
Member

Alkarex commented Jun 4, 2025

though what would be the use case for providing an URL instead of just downloading the desired favicon and uploading it?

I am not sure, but it could be interesting to have a bit of user feedback

Inverle and others added 2 commits June 6, 2025 14:57
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
Inverle added 2 commits June 7, 2025 00:20
I didn't even type it correctly, it was supposed to be 'customFavicon'
@johnsturgeon
Copy link

It's already limited to 1MiB but we could reduce it to 512KiB maybe

IMO 1MiB is enough to keep folks from pushing icons that are orders of magnitude too large. I think the idea of a 'limit' is really to prevent something bad from happening, and there might be scenarios where a file pushes over 500k

@Inverle Inverle marked this pull request as ready for review June 27, 2025 11:18
@Alkarex Alkarex merged commit 7915abd into FreshRSS:edge Jun 30, 2025
1 check passed
Inverle added a commit to Inverle/FreshRSS that referenced this pull request Jun 30, 2025
Alkarex pushed a commit that referenced this pull request Jun 30, 2025
* Rework #7646: `URLSearchParams` -> `JSON.stringify`

* Single quotes

* Send `id` as int
@Inverle Inverle deleted the favicon-upload branch July 7, 2025 15:39
@Inverle
Copy link
Member Author

Inverle commented Aug 6, 2025

#7792

alexlebens pushed a commit to alexlebens/infrastructure that referenced this pull request Aug 20, 2025
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [freshrss/freshrss](https://freshrss.org/) ([source](https://github.com/FreshRSS/FreshRSS)) | minor | `1.26.3` -> `1.27.0` |

---

### Release Notes

<details>
<summary>FreshRSS/FreshRSS (freshrss/freshrss)</summary>

### [`v1.27.0`](https://github.com/FreshRSS/FreshRSS/blob/HEAD/CHANGELOG.md#2025-08-18-FreshRSS-1270)

[Compare Source](FreshRSS/FreshRSS@1.26.3...1.27.0)

- Features
  - Implement support for HTTP `429 Too Many Requests` and `503 Service Unavailable`, obey `Retry-After` [#&#8203;7760](FreshRSS/FreshRSS#7760)
  - Add sort by category title, or by feed title [#&#8203;7702](FreshRSS/FreshRSS#7702)
  - Add search operator `c:` for categories like `c:23,34` or `!c:45,56` [#&#8203;7696](FreshRSS/FreshRSS#7696)
  - Custom feed favicons [#&#8203;7646](FreshRSS/FreshRSS#7646), [#&#8203;7704](FreshRSS/FreshRSS#7704), [#&#8203;7717](FreshRSS/FreshRSS#7717),
    [#&#8203;7792](FreshRSS/FreshRSS#7792)
  - Rework fetch favicons for fewer HTTP requests [#&#8203;7767](FreshRSS/FreshRSS#7767)
  - Add more unicity criteria based on title and/or content [#&#8203;7789](FreshRSS/FreshRSS#7789)
  - Automatically restore user configuration from backup [#&#8203;7682](FreshRSS/FreshRSS#7682)
  - API add support for states in `s` parameter of `streamId` [#&#8203;7695](FreshRSS/FreshRSS#7695)
  - Improve sharing via Print [#&#8203;7728](FreshRSS/FreshRSS#7728)
  - Redirect to the login page from bookmarklet instead of 403 [#&#8203;7782](FreshRSS/FreshRSS#7782)
  - Clean local cache more often, when refreshing feeds [#&#8203;7827](FreshRSS/FreshRSS#7827)
- Security
  - Implement reauthentication (*sudo* mode) [#&#8203;7753](FreshRSS/FreshRSS#7753)
  - Add `Content-Security-Policy: frame-ancestors` [#&#8203;7677](FreshRSS/FreshRSS#7677)
  - Ensure CSP everywhere [#&#8203;7810](FreshRSS/FreshRSS#7810)
  - Show warning when unsafe CSP policy is in use [#&#8203;7804](FreshRSS/FreshRSS#7804)
  - Fix access rights when creating a new user [#&#8203;7783](FreshRSS/FreshRSS#7783)
  - Improve security of form for user details [#&#8203;7771](FreshRSS/FreshRSS#7771), [#&#8203;7786](FreshRSS/FreshRSS#7786)
  - Disallow setting non-existent theme [#&#8203;7722](FreshRSS/FreshRSS#7722)
  - Regenerate cookie ID after logging out [#&#8203;7762](FreshRSS/FreshRSS#7762)
  - Require current password when setting new password [#&#8203;7763](FreshRSS/FreshRSS#7763)
  - Add missing access checks for feed-related actions [#&#8203;7768](FreshRSS/FreshRSS#7768)
  - Strip more unsafe attributes such as `referrerpolicy`, `ping` [#&#8203;7770](FreshRSS/FreshRSS#7770)
  - Remove unneeded execution permissions [#&#8203;7802](FreshRSS/FreshRSS#7802)
- Bug fixing
  - Fix redirections when scraping from HTML [#&#8203;7654](FreshRSS/FreshRSS#7654), [#&#8203;7741](FreshRSS/FreshRSS#7741)
  - Fix multiple authentication HTTP headers [#&#8203;7703](FreshRSS/FreshRSS#7703)
  - Fix HTML queries with a single feed [#&#8203;7730](FreshRSS/FreshRSS#7730)
  - WebSub: only perform a redirection when coming from WebSub [#&#8203;7738](FreshRSS/FreshRSS#7738)
  - Include enclosures in entries’ hash [#&#8203;7719](FreshRSS/FreshRSS#7719)
    - Negative side-effect: users of the option to *automatically mark updated articles as unread* will once have some articles with enclosures re-appear as unread
  - Fix cancellation of slider exit UI [#&#8203;7705](FreshRSS/FreshRSS#7705)
  - Honor *disable update* on update page [#&#8203;7733](FreshRSS/FreshRSS#7733)
  - Fix no registration limit setting [#&#8203;7751](FreshRSS/FreshRSS#7751)
  - Fix XML encoding of sharing functions [#&#8203;7822](FreshRSS/FreshRSS#7822)
- SimplePie
  - Fix propagation of HTTP error codes [#&#8203;7670](FreshRSS/FreshRSS#7670)
  - Fix support for XML feeds with HTML entities [#&#8203;7689](FreshRSS/FreshRSS#7689), [simplepie#915](simplepie/simplepie#915)
  - Fix feeds encoded in UTF-16LE [#&#8203;7691](FreshRSS/FreshRSS#7691), [simplepie#916](simplepie/simplepie#916)
  - Various upstream contributions [simplepie#917](simplepie/simplepie#917), [simplepie#924](simplepie/simplepie#924),
    [simplepie#926](simplepie/simplepie#926), [simplepie#932](simplepie/simplepie#932), [simplepie#933](simplepie/simplepie#933)
  - Sync upstream [#&#8203;7706](FreshRSS/FreshRSS#7706), [FreshRSS/simplepie#45](FreshRSS/simplepie#45), [#&#8203;7775](FreshRSS/FreshRSS#7775),
    [FreshRSS/simplepie#50](FreshRSS/simplepie#50), [#&#8203;7824](FreshRSS/FreshRSS#7824), [#&#8203;7825](FreshRSS/FreshRSS#7825),
  - Fix regex *Backtrack limit was exhausted* in `clean_hash()` [#&#8203;7813](FreshRSS/FreshRSS#7813), [FreshRSS/simplepie#48](FreshRSS/simplepie#48)
- Deployment
  - Docker default image (Debian 12 Bookworm) updated to PHP 8.2.29 [#&#8203;7805](FreshRSS/FreshRSS#7805)
  - Docker alternative image updated to Alpine 3.22 with PHP 8.4.11 and Apache 2.4.65 [#&#8203;7740](FreshRSS/FreshRSS#7740), [#&#8203;7740](FreshRSS/FreshRSS#7740),
    [#&#8203;7803](FreshRSS/FreshRSS#7803)
  - Start supporting PHP 8.5+ [#&#8203;7787](FreshRSS/FreshRSS#7787), [#&#8203;7826](FreshRSS/FreshRSS#7826)
    - Docker Alpine dev image `:newest` updated to PHP 8.5-alpha and Apache 2.4.65 [#&#8203;7773](FreshRSS/FreshRSS#7773)
  - Docker: interpolate `FRESHRSS_INSTALL` and `FRESHRSS_USER` variables [#&#8203;7725](FreshRSS/FreshRSS#7725)
  - Docker: Reduce how much data needs to be chown/chmod’ed on container startup [#&#8203;7793](FreshRSS/FreshRSS#7793)
  - Test for database PDO typing support during install (relevant for MySQL / MariaDB with obsolete driver) [#&#8203;7651](FreshRSS/FreshRSS#7651)
- Extensions
  - Add API endpoint for extensions [#&#8203;7576](FreshRSS/FreshRSS#7576)
  - Expose the reading modes for extensions [#&#8203;7668](FreshRSS/FreshRSS#7668), [#&#8203;7688](FreshRSS/FreshRSS#7688)
  - New extension hook `before_login_btn` [#&#8203;7761](FreshRSS/FreshRSS#7761)
- UI
  - Improve *mark as read* request showing popup due to `onbeforeunload` [#&#8203;7554](FreshRSS/FreshRSS#7554)
  - Fix lazy-loading for `<video poster="...">` and `<image>` [#&#8203;7636](FreshRSS/FreshRSS#7636)
  - Avoid styling `<code>` inside of `<pre>` [#&#8203;7797](FreshRSS/FreshRSS#7797)
  - Improve confirmation logic with `data-auto-leave-validation` [#&#8203;7785](FreshRSS/FreshRSS#7785)
  - Update `chart.js` to 4.5.0 [#&#8203;7752](FreshRSS/FreshRSS#7752), [#&#8203;7816](FreshRSS/FreshRSS#7816)
  - Various UI and style improvements: [#&#8203;7616](FreshRSS/FreshRSS#7616), [#&#8203;7811](FreshRSS/FreshRSS#7811)
- I18n
  - Show translation status in README [#&#8203;7715](FreshRSS/FreshRSS#7715)
  - Improve Indonesian [#&#8203;7654](FreshRSS/FreshRSS#7654), [#&#8203;7721](FreshRSS/FreshRSS#7721)
  - Improve Persian [#&#8203;7795](FreshRSS/FreshRSS#7795)
- Misc.
  - Improve PHP code [#&#8203;7642](FreshRSS/FreshRSS#7642), [#&#8203;7665](FreshRSS/FreshRSS#7665), [#&#8203;7761](FreshRSS/FreshRSS#7761),
    [#&#8203;7781](FreshRSS/FreshRSS#7781), [#&#8203;7794](FreshRSS/FreshRSS#7794)
  - Update dev dependencies [#&#8203;7708](FreshRSS/FreshRSS#7708), [#&#8203;7709](FreshRSS/FreshRSS#7709), [#&#8203;7710](FreshRSS/FreshRSS#7710),
    [#&#8203;7711](FreshRSS/FreshRSS#7711), [#&#8203;7776](FreshRSS/FreshRSS#7776), [#&#8203;7777](FreshRSS/FreshRSS#7777)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4zNS4wIiwidXBkYXRlZEluVmVyIjoiNDEuMzUuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW1hZ2UiXX0=-->

Reviewed-on: https://gitea.alexlebens.dev/alexlebens/infrastructure/pulls/1253
Co-authored-by: Renovate Bot <renovate-bot@alexlebens.net>
Co-committed-by: Renovate Bot <renovate-bot@alexlebens.net>
Alkarex pushed a commit that referenced this pull request Aug 31, 2025
Ref #7646

`<label>` needs to have an HTML input within or a `for=""` attribute

Changes proposed in this pull request:

- fixed 2 `<label>` who are not a 'real' label, so a `<div>` is the better choice


How to test the feature manually:

1. open feed configuration
2. click on "Icon" (first lines of config page) or "Export as OPML" (last lines of config page)
3. nothing will happen. Mouse cursor will not change while hovering over the labels.
Alkarex added a commit that referenced this pull request Nov 5, 2025
Rewrote the last Promise pattern of our code-base with an async/await pattern: [custom feed favicons](#7646)
Related to:
* #7962
Alkarex pushed a commit that referenced this pull request Nov 26, 2025
Quick fix for #8250
Regression from #7646

It seems the CSP is too permissive on some pages though (`img-src *`), so should fix that too later
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Allow Customize Favicon override for each feed Custom Favicons

5 participants