Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
180 changes: 77 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,80 @@
# © 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."
# )
import io
import json

from odoo.tests.common import HttpCase, tagged


@tagged('-at_install', 'post_install')
class TestAttachmentSizeLimit(HttpCase):
"""Tests HTTP for web_attachment_size_limit"""

@classmethod
def setUpClass(cls):
super().setUpClass()

# Définir une limite faible pour les tests (100 bytes)
cls.env['ir.config_parameter'].sudo().set_param(
'web_attachment_size_limit.max_upload_size', '100'
)

# Utilisateur courant
cls.user = cls.env.user

def _upload_file(self, content: bytes, filename='test.txt'):
"""Helper pour uploader un fichier via le contrôleur web"""
files = {
'ufile': (filename, io.BytesIO(content), 'text/plain'),
}
data = {
'model': 'res.users',
'id': str(self.user.id),
}

return self.url_open(
'/web/binary/upload_attachment',
data=data,
files=files,
)

def test_02_upload_too_large(self):
"""Upload > limite (200 bytes). Doit échouer."""
file_content = b'x' * 200

response = self._upload_file(
file_content,
filename='too_big.txt'
)

self.assertEqual(
response.status_code, 413,
'Upload should be rejected with HTTP 413'
)

payload = json.loads(response.text)
self.assertIn('error', payload)
self.assertIn('exceeds', payload['error'].lower())

def test_03_upload_success(self):
"""Upload < limite (50 bytes). Doit réussir."""
file_content = b'x' * 50

response = self._upload_file(
file_content,
filename='small_file.txt'
)

self.assertEqual(
response.status_code, 200,
'Upload should succeed'
)

payload = json.loads(response.text)
self.assertIn('id', payload)

attachment = self.env['ir.attachment'].browse(payload['id'])
self.assertTrue(attachment.exists())
self.assertEqual(attachment.res_model, 'res.users')
self.assertEqual(attachment.res_id, self.user.id)
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