Skip to content
2 changes: 1 addition & 1 deletion web_attachment_size_limit/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{
"name": "Web Attachment Size Limit",
"summary": "Enforce a global maximum size for file uploads",
"version": "14.0.1.0.0",
"version": "14.0.1.0.1",
"category": "Web",
"website": "https://numigi.com/r/home",
"author": "Numigi",
Expand Down
147 changes: 44 additions & 103 deletions web_attachment_size_limit/tests/test_attachment_limit.py
Original file line number Diff line number Diff line change
@@ -1,106 +1,47 @@
# © Numigi (tm) and all its contributors (https://numigi.com/r/home)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

# import io
# import json
# from odoo.tests.common import HttpCase, tagged
#
#
# @tagged('post_install', '-at_install')
# class TestAttachmentSizeLimit(HttpCase):
#
# def setUp(self):
# super(TestAttachmentSizeLimit, self).setUp()
# # Authenticate as admin to have upload rights and establish session
# self.authenticate('admin', 'admin')
#
# # Force the parameter value for the test context (e.g., 100 bytes)
# self.env['ir.config_parameter'].sudo().set_param(
# 'web.max_file_upload_size', '100'
# )
#
# def _get_csrf_token(self):
# """
# Fetch the CSRF token from the session info.
# This is required for controllers protected by
# @http.route(..., csrf=True)
# """
# # We invoke get_session_info via JSON-RPC to retrieve the token
# # url_open does not support 'json' param in this env, so we serialize manually
# response = self.url_open(
# '/web/session/get_session_info',
# data=json.dumps({}),
# headers={'Content-Type': 'application/json'}
# )
# return response.json().get('result', {}).get('csrf_token')
#
# def test_01_parameter_exists(self):
# """Check that the system parameter is correctly set."""
# param = self.env['ir.config_parameter'].sudo().get_param(
# 'web.max_file_upload_size'
# )
# self.assertEqual(
# param, '100', "The parameter value should be 100 for this test."
# )
#
# def test_02_upload_too_large(self):
# """Try to upload a file of 200 bytes (limit is 100). Should fail."""
#
# csrf_token = self._get_csrf_token()
#
# file_content = b'x' * 200
# files = {
# 'ufile': ('big_file.txt', io.BytesIO(file_content), 'text/plain'),
# }
# # We must send the model/id and CSRF token as data fields
# data = {
# 'model': 'res.users',
# 'id': str(self.env.user.id),
# 'csrf_token': csrf_token
# }
#
# # Note: /web/binary/upload_attachment is the standard upload URL
# # We use relative URL '/web/...' so Odoo uses the correct testing port
# response = self.url_open(
# '/web/binary/upload_attachment', data=data, files=files
# )
#
# response_content = response.content.decode('utf-8')
#
# self.assertIn(
# "File too large",
# response_content,
# "The upload should have been blocked with an error message. "
# "Response: %s" % response_content
# )
#
# def test_03_upload_success(self):
# """Try to upload a file of 50 bytes (limit is 100). Should succeed."""
#
# csrf_token = self._get_csrf_token()
#
# file_content = b'x' * 50
# files = {
# 'ufile': (
# 'small_file.txt', io.BytesIO(file_content), 'text/plain'
# ),
# }
# data = {
# 'model': 'res.users',
# 'id': str(self.env.user.id),
# 'csrf_token': csrf_token
# }
#
# response = self.url_open(
# '/web/binary/upload_attachment', data=data, files=files
# )
# response_content = response.content.decode('utf-8')
#
# self.assertNotIn(
# "error", response_content,
# "Valid upload should not return an error."
# )
# self.assertIn(
# "small_file.txt", response_content,
# "The filename should be in the response."
# )
from unittest.mock import MagicMock, patch

from odoo.tests.common import TransactionCase, tagged
from odoo.addons.web_attachment_size_limit.controllers.main import BinaryUploadLimit


@tagged("-at_install", "post_install")
class TestAttachmentSizeLimit(TransactionCase):

def setUp(self):
super().setUp()
self.env["ir.config_parameter"].sudo().set_param(
"web.max_file_upload_size", "100"
)
self.controller = BinaryUploadLimit()

def _test_upload(self, content_length: int):
ufile_mock = MagicMock()
ufile_mock.tell.return_value = content_length

patch_req = patch("odoo.addons.web_attachment_size_limit.controllers.main.request")
patch_sup = patch("odoo.addons.web.controllers.main.Binary.upload_attachment")

with patch_req as mock_req, patch_sup as mock_sup:
mock_req.env = self.env
mock_req.httprequest.files.getlist.return_value = [ufile_mock]
mock_sup.return_value = '{"id": 123}'

res = self.controller.upload_attachment(
model="res.users",
id=self.env.user.id,
ufile=ufile_mock,
)
return res, mock_sup.called

def test_02_upload_too_large(self):
response, super_called = self._test_upload(200)
assert "File too large" in response
assert not super_called

def test_03_upload_success(self):
response, super_called = self._test_upload(50)
assert response == '{"id": 123}'
assert super_called
13 changes: 8 additions & 5 deletions web_visual_company_switcher/controllers/company_switcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,17 @@ def get_companies_data(self):

result = {
'companies': companies_data,
'current_allowed_companies': [int(x) for x in session_allowed_ids], # Ensure int list
'current_company_id': int(current_company_id) # Ensure int
'current_allowed_companies': [int(x) for x in session_allowed_ids],
'current_company_id': int(current_company_id)
}

return result

except Exception as e:
return {'error': f'Failed to load companies: {str(e)}'}

@http.route('/web/visual_company_switcher/switch_company', type='json', auth='user', csrf=True)
@http.route('/web/visual_company_switcher/switch_company',
type='json', auth='user', csrf=True)
def switch_single_company(self, company_id):
"""Switch to a single company"""
try:
Expand All @@ -90,7 +91,8 @@ def switch_single_company(self, company_id):
except Exception as e:
return {'error': f'Failed to switch company: {str(e)}'}

@http.route('/web/visual_company_switcher/switch_companies', type='json', auth='user', csrf=True)
@http.route('/web/visual_company_switcher/switch_companies',
type='json', auth='user', csrf=True)
def switch_multiple_companies(self, company_ids):
"""Switch to multiple companies - mimics native Odoo multi-company behavior"""
try:
Expand All @@ -110,7 +112,8 @@ def switch_multiple_companies(self, company_ids):
if company not in user.company_ids:
return {'error': f'Access denied to company {company.name}'}

# This is the key: set allowed_company_ids in session to enable multi-company context
# This is the key: set allowed_company_ids in session
# to enable multi-company context
# This mimics exactly what Odoo's native company switcher does
request.session['allowed_company_ids'] = company_ids

Expand Down
Loading