diff --git a/addons/base_import_module/i18n/base_import_module.pot b/addons/base_import_module/i18n/base_import_module.pot index 8af3356b3f4..b46e0ea5dfa 100644 --- a/addons/base_import_module/i18n/base_import_module.pot +++ b/addons/base_import_module/i18n/base_import_module.pot @@ -132,6 +132,11 @@ msgstr "" msgid "No file sent." msgstr "" +#. module: base_import_module +#: model:ir.ui.view,arch_db:base_import_module.view_base_module_import +msgid "Note: you can only import data modules (.xml files and static assets)" +msgstr "" + #. module: base_import_module #: code:addons/base_import_module/controllers/main.py:25 #, python-format diff --git a/addons/base_import_module/views/base_import_module_view.xml b/addons/base_import_module/views/base_import_module_view.xml index 60af0d3fc61..b132c7f3a83 100644 --- a/addons/base_import_module/views/base_import_module_view.xml +++ b/addons/base_import_module/views/base_import_module_view.xml @@ -8,6 +8,7 @@
+

Note: you can only import data modules (.xml files and static assets)

- - + + @@ -229,8 +229,8 @@ - - + + diff --git a/addons/web_editor/static/lib/summernote/src/js/EventHandler.js b/addons/web_editor/static/lib/summernote/src/js/EventHandler.js index 1cb9cd8e1c7..d8dcbe18a8b 100644 --- a/addons/web_editor/static/lib/summernote/src/js/EventHandler.js +++ b/addons/web_editor/static/lib/summernote/src/js/EventHandler.js @@ -207,11 +207,15 @@ define([ var layoutInfo = dom.makeLayoutInfo(target); /* ODOO: (start_modification */ var $editable = layoutInfo.editable(); - if (!event.isDefaultPrevented()) { - modules.editor.saveRange($editable); + if (event.setStyleInfoFromEditable) { + var styleInfo = modules.editor.styleFromNode($editable); + } else { + if (!event.isDefaultPrevented()) { + modules.editor.saveRange($editable); + } + var styleInfo = modules.editor.currentStyle(target); } /* ODOO: end_modification) */ - var styleInfo = modules.editor.currentStyle(target); self.updateStyleInfo(styleInfo, layoutInfo); }, 0); }; diff --git a/addons/web_editor/static/src/js/backend.js b/addons/web_editor/static/src/js/backend.js index 1c5feb7a7d4..884ef68d674 100644 --- a/addons/web_editor/static/src/js/backend.js +++ b/addons/web_editor/static/src/js/backend.js @@ -57,7 +57,8 @@ var FieldTextHtmlSimple = widget.extend({ this.$translate.remove(); this.$translate = $(); // Triggers a mouseup to refresh the editor toolbar - this.$content.trigger('mouseup'); + var mouseupEvent = $.Event('mouseup', {'setStyleInfoFromEditable': true}); + this.$content.trigger(mouseupEvent); return def; }, initialize_content: function () { @@ -170,7 +171,11 @@ var FieldTextHtmlSimple = widget.extend({ transcoder.class_to_style(this.$content); transcoder.font_to_img(this.$content); } - this.internal_set_value(this.$content.html()); + var value = this.$content.html(); + if (this.get('value') === false && value === '


') { + value = false; + } + this.internal_set_value(value); }, destroy_content: function () { $(".oe-view-manager-content").off("scroll"); diff --git a/doc/cla/individual/naglis.md b/doc/cla/individual/naglis.md new file mode 100644 index 00000000000..dde081dfaa1 --- /dev/null +++ b/doc/cla/individual/naglis.md @@ -0,0 +1,11 @@ +Lithuania, 2019-02-09 + +I hereby agree to the terms of the Odoo Individual Contributor License +Agreement v1.0. + +I declare that I am authorized and able to make this agreement and sign this +declaration. + +Signed, + +Naglis Jonaitis naglis@mailbox.org https://github.com/naglis diff --git a/odoo/addons/base/ir/ir_model.py b/odoo/addons/base/ir/ir_model.py index 911692cd780..521a94238f5 100644 --- a/odoo/addons/base/ir/ir_model.py +++ b/odoo/addons/base/ir/ir_model.py @@ -443,6 +443,13 @@ class IrModelFields(models.Model): """ return self.sudo().search([('model', '=', model_name), ('name', '=', name)]) + @tools.ormcache('model_name', 'name') + def _get_id(self, model_name, name): + self.env.cr.execute("SELECT id FROM ir_model_fields WHERE model=%s AND name=%s", + (model_name, name)) + result = self.env.cr.fetchone() + return result and result[0] + @api.multi def _drop_column(self): tables_to_drop = set() diff --git a/odoo/addons/test_new_api/tests/test_new_fields.py b/odoo/addons/test_new_api/tests/test_new_fields.py index 1d982c4bb9b..6dc80305c7f 100644 --- a/odoo/addons/test_new_api/tests/test_new_fields.py +++ b/odoo/addons/test_new_api/tests/test_new_fields.py @@ -276,6 +276,16 @@ class TestFields(common.TransactionCase): self.assertEqual(record.bar, 'Ho') self.assertEqual(record.counts, {'compute': 0, 'inverse': 1}) + def test_13_inverse_access(self): + """ test access rights on inverse fields """ + foo = self.env['test_new_api.category'].create({'name': 'Foo'}) + user = self.env['res.users'].create({'name': 'Foo', 'login': 'foo'}) + self.assertFalse(user.has_group('base.group_system')) + # add group on non-stored inverse field + self.patch(type(foo).display_name, 'groups', 'base.group_system') + with self.assertRaises(AccessError): + foo.sudo(user).display_name = 'Forbidden' + def test_14_search(self): """ test search on computed fields """ discussion = self.env.ref('test_new_api.discussion_0') @@ -572,6 +582,10 @@ class TestFields(common.TransactionCase): self.env['ir.property'].create({'name': 'foo', 'fields_id': field.id, 'value': 'default', 'type': 'char'}) + # assumption: users don't have access to 'ir.property' + accesses = self.env['ir.model.access'].search([('model_id.model', '=', 'ir.property')]) + accesses.write(dict.fromkeys(['perm_read', 'perm_write', 'perm_create', 'perm_unlink'], False)) + # create/modify a record, and check the value for each user record = self.env['test_new_api.company'].create({'foo': 'main'}) record.invalidate_cache() @@ -591,6 +605,13 @@ class TestFields(common.TransactionCase): self.assertEqual(record.sudo(user1).foo, False) self.assertEqual(record.sudo(user2).foo, 'default') + # set field with 'force_company' in context + record.sudo(user0).with_context(force_company=company1.id).foo = 'beta' + record.invalidate_cache() + self.assertEqual(record.sudo(user0).foo, 'main') + self.assertEqual(record.sudo(user1).foo, 'beta') + self.assertEqual(record.sudo(user2).foo, 'default') + # create company record and attribute company_record = self.env['test_new_api.company'].create({'foo': 'ABC'}) attribute_record = self.env['test_new_api.company.attr'].create({ @@ -610,6 +631,26 @@ class TestFields(common.TransactionCase): self.assertEqual(attribute_record.bar, 'DEFDEF') self.assertFalse(self.env.has_todo()) + # add group on company-dependent field + self.assertFalse(user0.has_group('base.group_system')) + self.patch(type(record).foo, 'groups', 'base.group_system') + with self.assertRaises(AccessError): + record.sudo(user0).foo = 'forbidden' + + user0.write({'groups_id': [(4, self.env.ref('base.group_system').id)]}) + record.sudo(user0).foo = 'yes we can' + + # add ir.rule to prevent access on record + self.assertTrue(user0.has_group('base.group_user')) + model_id = self.env['ir.model'].search([('model', '=', record._name)]).id + rule = self.env['ir.rule'].create({ + 'model_id': model_id, + 'groups': [self.env.ref('base.group_user').id], + 'domain_force': str([('id', '!=', record.id)]), + }) + with self.assertRaises(AccessError): + record.sudo(user0).foo = 'forbidden' + def test_28_sparse(self): """ test sparse fields. """ record = self.env['test_new_api.sparse'].create({}) diff --git a/odoo/fields.py b/odoo/fields.py index a4f6bc7e27b..511fa0a3235 100644 --- a/odoo/fields.py +++ b/odoo/fields.py @@ -661,13 +661,25 @@ class Field(object): return model.env['ir.property'].get(self.name, self.model_name) def _compute_company_dependent(self, records): - Property = records.env['ir.property'] + # read property as superuser, as the current user may not have access + context = records.env.context + if 'force_company' not in context: + field_id = records.env['ir.model.fields']._get_id(self.model_name, self.name) + company = records.env['res.company']._company_default_get(self.model_name, field_id) + context = dict(context, force_company=company.id) + Property = records.env(user=SUPERUSER_ID, context=context)['ir.property'] values = Property.get_multi(self.name, self.model_name, records.ids) for record in records: record[self.name] = values.get(record.id) def _inverse_company_dependent(self, records): - Property = records.env['ir.property'] + # update property as superuser, as the current user may not have access + context = records.env.context + if 'force_company' not in context: + field_id = records.env['ir.model.fields']._get_id(self.model_name, self.name) + company = records.env['res.company']._company_default_get(self.model_name, field_id) + context = dict(context, force_company=company.id) + Property = records.env(user=SUPERUSER_ID, context=context)['ir.property'] values = { record.id: self.convert_to_write(record[self.name], record) for record in records diff --git a/odoo/models.py b/odoo/models.py index 92e6cd7c3d6..706fc3b2a7c 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -3533,10 +3533,15 @@ class BaseModel(object): protected_fields = map(self._fields.get, new_vals) with self.env.protecting(protected_fields, self): # write old-style fields with (low-level) method _write - if old_vals: + if old_vals or new_vals: + # if log_access is enabled, this updates 'write_date' and + # 'write_uid' and check access rules, even when old_vals is + # empty self._write(old_vals) if new_vals: + self.check_field_access_rights('write', list(new_vals)) + self.modified(set(new_vals) - set(old_vals)) # put the values of fields into cache, and inverse them