Add model, record and records attribute on Wizard

issue9421
review309861012
This commit is contained in:
Cédric Krier
2020-07-07 23:56:10 +02:00
parent 5ca5b373ae
commit 060a9da6bf
8 changed files with 122 additions and 22 deletions
+2
View File
@@ -1,3 +1,5 @@
* Add model, record and records attributes on Wizard
* Check read access of wizard records
* Add cached_property in tools
* Use custom class to store record data
* Do not write to existing targets on xxx2Many add
+15
View File
@@ -45,6 +45,21 @@ Class attributes are:
It contains a dictionary with state name as key and :class:`State` as
value.
.. attribute:: model
It contains the :class:`~trytond.model.Model` class on which the wizard is
executed.
.. attribute:: record
It contains the :class:`~trytond.model.Model` instance on which the wizard
is executed.
.. attribute:: records
It contains the list of :class:`~trytond.model.Model` instances on which
the wizard is executed.
Class methods are:
.. classmethod:: Wizard.__setup__()
+11
View File
@@ -1046,6 +1046,17 @@ class ActionWizard(ActionMixin, ModelSQL, ModelView):
def default_type():
return 'ir.action.wizard'
@classmethod
def get_models(cls, name, action_id=None):
# TODO add cache
domain = [
(cls._action_name, '=', name),
]
if action_id:
domain.append(('id', '=', action_id))
actions = cls.search(domain)
return {a.model for a in actions if a.model}
class ActionURL(ActionMixin, ModelSQL, ModelView):
"Action URL"
+8
View File
@@ -311,5 +311,13 @@ this repository contains the full copyright notices and license terms. -->
<record model="ir.message" id="msg_notes">
<field name="text">Notes</field>
</record>
<record model="ir.message" id="msg_access_wizard_error">
<field name="text">You are not allowed to execute wizard "%(wizard)s".</field>
</record>
<record model="ir.message" id="msg_access_wizard_model_error">
<field name="text">You are not allowed to execute wizard "%(wizard)s" on "%(model)s".</field>
</record>
</data>
</tryton>
+12 -18
View File
@@ -950,11 +950,10 @@ class TranslationSet(Wizard):
pool = Pool()
Report = pool.get('ir.action.report')
Translation = pool.get('ir.translation')
context = Transaction().context
if context.get('active_model') == Report.__name__:
reports = Report.browse(context.get('active_ids', []))
elif context.get('active_model', 'ir.ui.menu') == 'ir.ui.menu':
if self.model == Report:
reports = self.records
elif not self.model or self.model.__name__ == 'ir.ui.menu':
with Transaction().set_context(active_test=False):
reports = Report.search([('translatable', '=', True)])
else:
@@ -1045,11 +1044,10 @@ class TranslationSet(Wizard):
pool = Pool()
View = pool.get('ir.ui.view')
Translation = pool.get('ir.translation')
context = Transaction().context
if context.get('active_model') == View.__name__:
views = View.browse(context.get('active_ids', []))
elif context.get('active_model', 'ir.ui.menu') == 'ir.ui.menu':
if self.model == View:
views = self.records
elif not self.model or self.model.__name__ == 'ir.ui.menu':
with Transaction().set_context(active_test=False):
views = View.search([])
else:
@@ -1357,19 +1355,18 @@ class TranslationUpdate(Wizard):
Translation = pool.get('ir.translation')
Report = pool.get('ir.action.report')
View = pool.get('ir.ui.view')
context = Transaction().context
cursor = Transaction().connection.cursor()
cursor_update = Transaction().connection.cursor()
translation = Translation.__table__()
lang = self.start.language.code
parent_lang = get_parent(lang)
if context.get('active_model') == Report.__name__:
reports = Report.browse(context.get('active_ids', []))
if self.model == Report:
reports = self.records
source_clause = ((translation.type == 'report')
& translation.name.in_([r.report_name for r in reports]))
elif context.get('active_model') == View.__name__:
views = View.browse(context.get('active_ids', []))
elif self.model == View:
views = self.records
source_clause = ((translation.type == 'view')
& translation.name.in_([v.model for v in views]))
else:
@@ -1420,7 +1417,7 @@ class TranslationUpdate(Wizard):
& (translation.module == row['module'])
& (translation.lang == lang)))
if context.get('active_model') in {Report.__name__, View.__name__}:
if self.model in {Report, View}:
return
columns = [translation.name.as_('name'),
@@ -1592,11 +1589,8 @@ class TranslationReport(Wizard):
open_ = StateAction('ir.act_translation_report')
def do_open_(self, action):
pool = Pool()
Report = pool.get('ir.action.report')
context = Transaction().context
assert context['active_model'] == Report.__name__
reports = Report.browse(context['active_ids'])
reports = self.records
action['pyson_domain'] = PYSONEncoder().encode([
('type', '=', 'report'),
('name', 'in', [r.report_name for r in reports]),
+26
View File
@@ -127,6 +127,32 @@ class WizardTestCase(unittest.TestCase):
with Transaction().set_context(active_model='test.access'):
Wizard.execute(session_id, {}, start_state)
@with_transaction()
def test_execute_without_read_access(self):
"Execute wizard without read access"
pool = Pool()
Wizard = pool.get('test.test_wizard', type='wizard')
session_id, start_state, end_state = Wizard.create()
with self.assertRaises(AccessError):
with Transaction().set_context(
active_model='test.access', active_id=1):
Wizard.execute(session_id, {}, start_state)
@with_transaction()
def test_execute_wrong_model(self):
"Execute wizard on wrong model"
pool = Pool()
Wizard = pool.get('test.test_wizard', type='wizard')
session_id, start_state, end_state = Wizard.create()
with self.assertRaises(AccessError):
with Transaction().set_context(
active_model='test.test_wizard.start'):
Wizard.execute(session_id, {}, start_state)
def suite():
return unittest.TestLoader().loadTestsFromTestCase(WizardTestCase)
+6
View File
@@ -15,5 +15,11 @@ this repository contains the full copyright notices and license terms. -->
]]>
</field>
</record>
<record model="ir.action.wizard" id="test_wizard">
<field name="name">Test Wizard</field>
<field name="wiz_name">test.test_wizard</field>
<field name="model">test.access</field>
</record>
</data>
</tryton>
+42 -4
View File
@@ -8,15 +8,17 @@ __all__ = ['Wizard',
import json
import copy
from trytond.i18n import gettext
from trytond.pool import Pool, PoolBase
from trytond.transaction import Transaction
from trytond.url import URLMixin
from trytond.protocols.jsonrpc import JSONDecoder, JSONEncoder
from trytond.model import ModelSQL
from trytond.model.exceptions import AccessError
from trytond.model.fields import states_validate
from trytond.pyson import PYSONEncoder
from trytond.rpc import RPC
from trytond.exceptions import UserError
from trytond.tools import cached_property
class Button(object):
@@ -233,21 +235,38 @@ class Wizard(URLMixin, PoolBase):
with Transaction().set_context(_check_access=True):
model = context.get('active_model')
if model:
Model = pool.get(model)
if model and model != 'ir.ui.menu':
ModelAccess.check(model, 'read')
models = ActionWizard.get_models(
cls.__name__, action_id=context.get('action_id'))
if model and models and model not in models:
raise AccessError(gettext(
'ir.msg_access_wizard_model_error',
wizard=cls.__name__,
model=model))
groups = set(User.get_groups())
wizard_groups = ActionWizard.get_groups(cls.__name__,
action_id=context.get('action_id'))
if wizard_groups:
if not groups & wizard_groups:
raise UserError('Calling wizard %s is not allowed!'
% cls.__name__)
raise AccessError(gettext(
'ir.msg_access_wizard_error',
name=cls.__name__))
elif model and model != 'ir.ui.menu':
Model = pool.get(model)
if (not callable(getattr(Model, 'table_query', None))
or Model.write.__func__ != ModelSQL.write.__func__):
ModelAccess.check(model, 'write')
if model:
ids = context.get('active_ids') or []
id_ = context.get('active_id')
if id_ not in ids:
ids.append(id_)
# Check read access
Model.read(ids, ['id'])
@classmethod
def create(cls):
"Create a session"
@@ -354,3 +373,22 @@ class Wizard(URLMixin, PoolBase):
Session.write([session], {
'data': data,
})
@cached_property
def model(self):
pool = Pool()
context = Transaction().context
if context.get('active_model'):
return pool.get(context['active_model'])
@cached_property
def record(self):
context = Transaction().context
if context.get('active_id'):
return self.model(context['active_id'])
@cached_property
def records(self):
context = Transaction().context
if context.get('active_ids'):
return self.model.browse(context['active_ids'])