Add model, record and records attribute on Wizard
issue9421 review309861012
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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__()
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
@@ -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]),
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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'])
|
||||
|
||||
Reference in New Issue
Block a user