diff --git a/trytond/CHANGELOG b/trytond/CHANGELOG index 5227199b9a..bbf592775a 100644 --- a/trytond/CHANGELOG +++ b/trytond/CHANGELOG @@ -1,3 +1,4 @@ +* Add check_modification to ModelStorage * Add on_modification to ModelStorage * Add preprocess_values to ModelStorage * Remove trigger methods on ModelStorage diff --git a/trytond/doc/ref/models.rst b/trytond/doc/ref/models.rst index 5968b4e0b6..80f61d3bae 100644 --- a/trytond/doc/ref/models.rst +++ b/trytond/doc/ref/models.rst @@ -414,6 +414,17 @@ Class methods: Delete records. +.. classmethod:: ModelStorage.check_modification(mode, record[, values[, external]]) + + Method called after ``records`` are created and before ``records`` are + modified with ``values`` or deleted. + ``external`` specifies whether remote access check must be enforced. + + .. note:: + + It is used to prevent some kind of modification by raising an + :exc:`~trytond.model.exceptions.AccessError`. + .. classmethod:: ModelStorage.on_modification(mode, records[, field_names]) Method called after ``records`` have been modified. diff --git a/trytond/trytond/model/modelstorage.py b/trytond/trytond/model/modelstorage.py index a8bed7d4d4..c8c36ba2c8 100644 --- a/trytond/trytond/model/modelstorage.py +++ b/trytond/trytond/model/modelstorage.py @@ -280,24 +280,27 @@ class ModelStorage(Model): raise NotImplementedError @classmethod - @without_check_access def _after_create(cls, ids): Trigger = Pool().get('ir.trigger') transaction = Transaction() + check_access = transaction.user and transaction.check_access - triggers = Trigger.get_triggers(cls.__name__, 'create') + with without_check_access(): + triggers = Trigger.get_triggers(cls.__name__, 'create') - for sub_ids in grouped_slice(ids, record_cache_size(transaction)): - records = cls.browse(sub_ids) - cls._validate(records) - cls._compute_fields(records) - cls.on_modification('create', records) - if triggers: - for trigger in triggers: - trigger.queue_trigger_action(records) + for sub_ids in grouped_slice(ids, record_cache_size(transaction)): + records = cls.browse(sub_ids) + cls._validate(records) + cls.check_modification( + 'create', records, external=check_access) + cls._compute_fields(records) + cls.on_modification('create', records) + if triggers: + for trigger in triggers: + trigger.queue_trigger_action(records) - transaction.create_records[cls.__name__].update(ids) - return ids + transaction.create_records[cls.__name__].update(ids) + return ids @classmethod def _before_read(cls, ids, fields_names): @@ -347,6 +350,8 @@ class ModelStorage(Model): cls.__check_xml_record(records, values) ModelFieldAccess.check(cls.__name__, values.keys(), 'write') with without_check_access(): + cls.check_modification( + 'write', records, values, external=check_access) args.append(records) args.append(cls.preprocess_values('write', values)) if check_access and values: @@ -420,6 +425,7 @@ class ModelStorage(Model): ModelAccess.check(cls.__name__, 'delete') cls.__check_xml_record(records, None) with without_check_access(): + cls.check_modification('delete', records, external=check_access) if ModelData.has_model(cls.__name__): ModelData.clean(records) if check_access: @@ -479,6 +485,10 @@ class ModelStorage(Model): for f in cls._fields.values()): Translation.delete_ids(cls.__name__, 'model', ids) + @classmethod + def check_modification(cls, mode, records, values=None, external=False): + assert mode in {'create', 'write', 'delete'} + @classmethod def on_modification(cls, mode, records, field_names=None): assert mode in {'create', 'write', 'delete'}