Repository navigation
Conversation
closes OCA#199
Currently translated at 100,0% (9 of 9 strings) Translation: web-11.0/web-11.0-web_favicon Translate-URL: https://translation.odoo-community.org/projects/web-11-0/web-11-0-web_favicon/pt_BR/
Updated by Update PO files to match POT (msgmerge) hook in Weblate.
Currently translated at 22.2% (2 of 9 strings) Translation: web-12.0/web-12.0-web_favicon Translate-URL: https://translation.odoo-community.org/projects/web-12-0/web-12-0-web_favicon/pt/
Currently translated at 100.0% (9 of 9 strings) Translation: web-12.0/web-12.0-web_favicon Translate-URL: https://translation.odoo-community.org/projects/web-12-0/web-12-0-web_favicon/zh_CN/
Currently translated at 100.0% (9 of 9 strings) Translation: web-12.0/web-12.0-web_favicon Translate-URL: https://translation.odoo-community.org/projects/web-12-0/web-12-0-web_favicon/pt_BR/
Currently translated at 100.0% (9 of 9 strings) Translation: web-12.0/web-12.0-web_favicon Translate-URL: https://translation.odoo-community.org/projects/web-12-0/web-12-0-web_favicon/ca/
Currently translated at 100.0% (9 of 9 strings) Translation: web-12.0/web-12.0-web_favicon Translate-URL: https://translation.odoo-community.org/projects/web-12-0/web-12-0-web_favicon/es/
Currently translated at 100.0% (9 of 9 strings) Translation: web-12.0/web-12.0-web_favicon Translate-URL: https://translation.odoo-community.org/projects/web-12-0/web-12-0-web_favicon/tr/
Currently translated at 88.8% (8 of 9 strings) Translation: web-12.0/web-12.0-web_favicon Translate-URL: https://translation.odoo-community.org/projects/web-12-0/web-12-0-web_favicon/de/
Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: web-18.0/web-18.0-web_favicon Translate-URL: https://translation.odoo-community.org/projects/web-18-0/web-18-0-web_favicon/
Assisted-by: Claude Opus 5.5
f68dca7 to
7826064
Compare
|
@legalsylvain I think they can be legit. For example, this one seems OK in a code review. I will reopen some of them and review them. They are not against our AI policy, as they are being reviewed after the AI pass and assuming the responsibility. |
|
Hi @pedrobaeza. I don't know ! The current AI Policy https://github.com/OCA/.github/blob/master/AI_POLICY.md#policy says "Accounts exhibiting bot-like behaviour - automated mass pull request creation or reviews - will be permanently banned, whether they belong to a human or not." (proposed by @etobella here : OCA/.github#5) This github account :
Maybe it's a matter of interpretation, but for me 689 PR per day == "totally unsupervised spam". |
|
Yeah, of course that's something unsupervised, but it's not inside OCA. About OCA PRs, a couple of them that I have checked are not the typical AI slop, so this may worth to check. |
|
@legalsylvain As I mentioned I did a first pass to migrate all OCA repos against my own account. Then I spent all my weekend reviewing modules one by one and opening a PR after manual testing and adjustment on each. That's why I left some of them as draft until I have time next weekend. This one for example was left as draft since it's failing some checks and needs updating the test cases. I stopped contributing to OCA long ago since I know how things can be slow over here, understandably or not. e-g. OCA/social#425. I wasn't using this account either for the last 4 years since I tend to use separate accounts for each company I work for. I'll stop creating PRs as requested. I'll be maintaining the forks instead for the time being. |
|
Khalid, the main thing is to not bring slop, but your contributions are welcome as the rest. You can also complement them reviewing other PRs for speeding up the merge process. |
|
I've been using a small diff script to keep an eye on #lines changes, then checking if non-zero. Usually it's the tests suites missing, others like OCA/account-analytic#988 can be explained by Odoo dropping the stock.scrap models. The only time when I'm not checking the code thoroughly is when the diff is 0 (excluding indentation and formatting) or missing test cases. In all cases I'm functionally testing each module on a fresh database before submitting a PR. |
There was a problem hiding this comment.
_get_favicon calls int() on the cids cookie, so cids=abc raises ValueError and breaks /web/login, the error page and /favicon.ico. Using self.env.company (already validated by Odoo) removes the parsing, and bin_size=True avoids loading the file on every render. The template should also keep core's x_icon first, which makes the website_id branch redundant.
TT64891
|
The following commit authors need to sign the Contributor License Agreement: |
|
@remi-filament @tarteo @hbrunn @rov-adhoc can you check the CLA and resign with this emails 🙏 |
cristina-hidalgo-tecnativa
left a comment
There was a problem hiding this comment.
_get_favicon no longer reads the cids cookie and uses self.env.company instead, so the tests must set the current company with with_company() instead of mocking the cookie.
test_04 still expects the first company of the cookie and fails ('16' not found in '/web/image/res.company/1/favicon...').
test_01 only passes by chance, and MockRequest is no longer needed. Asserting on /res.company/<id>/favicon is also stricter than assertIn(str(id), url), which any 1 in the URL satisfies.
There was a problem hiding this comment.
I tested this PR (head 22167e6) on a local 20.0 database and compared it with 19.0, with real HTTP requests and the module tests (with and without website installed).
The main point is that the backend favicon no longer follows the active company (see the comment on _get_favicon). Details and suggestions inline.
|
|
||
| import odoo | ||
| from odoo import api, fields, models, tools | ||
| from odoo.tools.binary import BinaryBytes |
There was a problem hiding this comment.
Needed by the suggestion on _get_favicon below.
| from odoo.tools.binary import BinaryBytes | |
| from odoo.http import request | |
| from odoo.tools.binary import BinaryBytes |
| """Returns a local url that points to the favicon of the current company.""" | ||
| company = self.env.company.sudo() | ||
| # bin_size avoids reading the whole file just to know if it exists | ||
| if not company.with_context(bin_size=True).favicon: |
There was a problem hiding this comment.
The favicon no longer follows the active company (regression from 19.0).
When Odoo renders an HTML page (/odoo, /web/login), it does not load the cids cookie into allowed_company_ids, so self.env.company is always the user's main company (odoo/orm/environments.py, company). Core reads the cookie by hand when it needs the active company in an HTML route (e.g. _get_active_company in addons/hr_attendance/controllers/main.py).
Tested with an admin user, main company 1, allowed companies 1 and B:
| Request | 19.0 | 20.0 (this PR) |
|---|---|---|
/odoo, cids=B |
/web/image/res.company/B/favicon |
/web/image/res.company/1/favicon |
/odoo, cids=B-1 |
/web/image/res.company/B/favicon |
/web/image/res.company/1/favicon |
The int() problem is real (cids=abc gives a 500 in 19.0), but we think the fix is to read the cookie safely, not to stop reading it. We tested this suggestion on a local 20.0 database: cids=B and B-1 give company B, 1-B gives company 1, and abc or a company the user is not allowed in falls back to self.env.company.
Also, the bin_size context is not used anymore in 20.0 (no reference in odoo/), and reading company.favicon is already lazy, so the with_context and its comment can go.
| """Returns a local url that points to the favicon of the current company.""" | |
| company = self.env.company.sudo() | |
| # bin_size avoids reading the whole file just to know if it exists | |
| if not company.with_context(bin_size=True).favicon: | |
| """Returns a local url that points to the favicon of the active company.""" | |
| company = self.env.company | |
| cids = request and request.cookies.get("cids") | |
| first = cids.split("-")[0] if cids else "" | |
| if first.isdigit() and int(first) in self.env.user._get_company_ids(): | |
| company = self.env["res.company"].browse(int(first)) | |
| company = company.sudo() | |
| if not company.favicon: |
| favicon = fields.Binary( | ||
| string="Company Favicon", | ||
| help="This field holds the image used to display favicon for a given company.", | ||
| default=lambda self: self._get_default_favicon(), |
There was a problem hiding this comment.
Minor: on install, all existing companies get the same favicon.
In 20.0, Binary.update_db fills existing records with the field default when the field has one (update_db_binary_attachment in odoo/orm/fields_binary.py), using one value for all of them. So all existing companies (except the main one, set by the XML) get the same favicon with the same color bar. In 19.0 they stayed empty. The create override already sets a favicon on new companies, so removing default= gives back the 19.0 behavior.
| default=lambda self: self._get_default_favicon(), |
| def test_03_website_favicon(self): | ||
| """Test if favicon URL is correctly returned when website_id is in context.""" | ||
| if self.env["ir.module.module"].search( | ||
| [("name", "=", "website"), ("state", "=", "installed")] | ||
| ): | ||
| company = self.env["res.company"].create( | ||
| { | ||
| "name": "Test Company with Website", | ||
| } | ||
| ) | ||
| website = self.env["website"].create( | ||
| { | ||
| "name": "Test Website", | ||
| "domain": "www.test.com", | ||
| "company_id": company.id, | ||
| } | ||
| ) | ||
| website.favicon = website._default_favicon() | ||
| favicon_url = company.with_context(website_id=website.id)._get_favicon() | ||
| expected_favicon_url = website.image_url(website, "favicon") | ||
| self.assertEqual( | ||
| favicon_url, | ||
| expected_favicon_url, | ||
| "The favicon URL should match the expected value.", | ||
| ) | ||
|
|
There was a problem hiding this comment.
test_03 only runs when website is installed, which is not the case in CI. With website installed it fails, because the website_id branch was removed from _get_favicon. Since x_icon now goes first in the template, core covers the website case (website.layout sets x_icon), so this test can be removed.
| def test_03_website_favicon(self): | |
| """Test if favicon URL is correctly returned when website_id is in context.""" | |
| if self.env["ir.module.module"].search( | |
| [("name", "=", "website"), ("state", "=", "installed")] | |
| ): | |
| company = self.env["res.company"].create( | |
| { | |
| "name": "Test Company with Website", | |
| } | |
| ) | |
| website = self.env["website"].create( | |
| { | |
| "name": "Test Website", | |
| "domain": "www.test.com", | |
| "company_id": company.id, | |
| } | |
| ) | |
| website.favicon = website._default_favicon() | |
| favicon_url = company.with_context(website_id=website.id)._get_favicon() | |
| expected_favicon_url = website.image_url(website, "favicon") | |
| self.assertEqual( | |
| favicon_url, | |
| expected_favicon_url, | |
| "The favicon URL should match the expected value.", | |
| ) |
| def test_04_favicon_multiple_companies(self): | ||
| """Test _get_favicon with multiple companies in cids cookie.""" | ||
| Company = self.env["res.company"] | ||
|
|
||
| company_1 = Company.create( | ||
| {"name": "Company 1", "favicon": Company._get_default_favicon()} | ||
| ) | ||
| company_2 = Company.create( | ||
| {"name": "Company 2", "favicon": Company._get_default_favicon()} | ||
| ) | ||
| company_3 = Company.create( | ||
| {"name": "Company 3", "favicon": Company._get_default_favicon()} | ||
| ) | ||
|
|
||
| with MockRequest(self.env) as mock_request: | ||
| mock_request.cookies = { | ||
| "cids": f"{company_1.id}-{company_2.id}-{company_3.id}" | ||
| } | ||
| favicon_url = Company._get_favicon() | ||
|
|
||
| self.assertTrue( | ||
| favicon_url, "Favicon URL should be generated for multiple companies." | ||
| ) | ||
| self.assertIn( | ||
| str(company_1.id), | ||
| favicon_url, | ||
| "Favicon URL should correspond to the first company in the cids cookie.", | ||
| ) |
There was a problem hiding this comment.
A test based on with_company() would pass even without the fix on _get_favicon, because a real request does not go through that path. MockRequest accepts cookies= in 20.0, so the test can keep checking the cookie.
With the _get_favicon suggestion applied, these tests pass on a local 20.0 database, with and without website installed. With the current code, test_04 fails.
| def test_04_favicon_multiple_companies(self): | |
| """Test _get_favicon with multiple companies in cids cookie.""" | |
| Company = self.env["res.company"] | |
| company_1 = Company.create( | |
| {"name": "Company 1", "favicon": Company._get_default_favicon()} | |
| ) | |
| company_2 = Company.create( | |
| {"name": "Company 2", "favicon": Company._get_default_favicon()} | |
| ) | |
| company_3 = Company.create( | |
| {"name": "Company 3", "favicon": Company._get_default_favicon()} | |
| ) | |
| with MockRequest(self.env) as mock_request: | |
| mock_request.cookies = { | |
| "cids": f"{company_1.id}-{company_2.id}-{company_3.id}" | |
| } | |
| favicon_url = Company._get_favicon() | |
| self.assertTrue( | |
| favicon_url, "Favicon URL should be generated for multiple companies." | |
| ) | |
| self.assertIn( | |
| str(company_1.id), | |
| favicon_url, | |
| "Favicon URL should correspond to the first company in the cids cookie.", | |
| ) | |
| def test_04_favicon_active_company(self): | |
| """The favicon URL follows the first company of the cids cookie.""" | |
| Company = self.env["res.company"] | |
| company_1 = Company.create({"name": "Company 1"}) | |
| company_2 = Company.create({"name": "Company 2"}) | |
| cookie = f"{company_2.id}-{company_1.id}" | |
| with MockRequest(self.env, cookies={"cids": cookie}): | |
| favicon_url = Company._get_favicon() | |
| self.assertIn(f"/res.company/{company_2.id}/favicon", favicon_url) | |
| def test_05_favicon_invalid_cookie(self): | |
| """An invalid or not allowed company falls back to the current one.""" | |
| Company = self.env["res.company"] | |
| for cookie in ("abc", "999999"): | |
| with MockRequest(self.env, cookies={"cids": cookie}): | |
| favicon_url = Company._get_favicon() | |
| self.assertIn(f"/res.company/{self.env.company.id}/favicon", favicon_url) |
22167e6 to
8f82cbf
Compare
Co-authored-by: Luciano Esperlazza <257575630+les-adhoc@users.noreply.github.com> Assisted-by: Claude Opus 5.5
8f82cbf to
8868906
Compare
Migration
Migrate
web_faviconto Odoo 20.0. The module provides company-specific favicon customization for backend and portal pages.Testing
web_favicontest methods passed; Odoo reported 0 failures and 0 errors across 6 tests on a fresh test database./web/image/res.company/1/favicon?unique=a054748.Migration and first testing pass: Claude Opus 5.5.
Manual testing and PR preparation: Me + Copilot Auto.