[IMP] sm modules: "neutralise" genders
Purpose ======= Change all masculine nouns in Odoo's code to neutral nouns (when possible), making sure that demo data is correctly handled. This is particularly important since our code is open source, and nowadays lots of machine learning models are trained on open source repositories. With this small change we contribute to training more "fair" models, and teaching models that "employee" or "user" != "he". This also affects some text visible by the user, hence making it more inclusive for Odoo users. Task-2853046 closes odoo/odoo#91292 Related: odoo/enterprise#27302 Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
2FA Invite mail
|
||||
===============
|
||||
Allow the users to invite another user to use Two-Factor authentication
|
||||
by sending an email to the target user. This email redirect him to :
|
||||
by sending an email to the target user. This email redirect them to :
|
||||
- the users security settings if the user is internal.
|
||||
- the portal security settings page if the user is not internal.
|
||||
""",
|
||||
|
||||
@@ -46,7 +46,7 @@ tour.register('totp_admin_self_invite', {
|
||||
content: "go to Account security Tab",
|
||||
trigger: "a.nav-link:contains(Account Security)",
|
||||
}, {
|
||||
content: "check that user cannot invite himself to use 2FA.",
|
||||
content: "check that user cannot invite themselves to use 2FA.",
|
||||
trigger: "body",
|
||||
run: function () {
|
||||
var $inviteBtn = $('button:contains(Invite to use 2FA)');
|
||||
|
||||
@@ -1892,7 +1892,7 @@ class Lead(models.Model):
|
||||
# Each won/lost lead increments a frequency table, where we store, for each field/value couple, the number of
|
||||
# won and lost leads.
|
||||
# E.g. : A won lead from Belgium will increase the won count of the frequency country_id='Belgium' by 1.
|
||||
# The frequencies are split by team_id, so each team has his own frequencies environment. (Team A doesn't impact B)
|
||||
# The frequencies are split by team_id, so each team has its own frequencies environment. (Team A doesn't impact B)
|
||||
# There are two main ways to build the frequency table:
|
||||
# - Live Increment: At each Won/lost, we increment directly the frequencies based on the lead values.
|
||||
# Done right BEFORE writing the lead as won or lost.
|
||||
@@ -1980,7 +1980,7 @@ class Lead(models.Model):
|
||||
field = frequency['variable']
|
||||
value = frequency['value']
|
||||
|
||||
# To avoid that a tag take to much importance if his subset is too small,
|
||||
# To avoid that a tag take too much importance if its subset is too small,
|
||||
# we ignore the tag frequencies if we have less than 50 won or lost for this tag.
|
||||
if field == 'tag_id' and (frequency['won_count'] + frequency['lost_count']) < 50:
|
||||
continue
|
||||
@@ -2066,7 +2066,7 @@ class Lead(models.Model):
|
||||
final state of the lead.
|
||||
This issue is when the lead leaves a closed state because once the new values have been writen, we do not know
|
||||
what was the previous state that we need to decrement.
|
||||
This is why 'is_won' and 'decrement' parameters are used to describe the from / to change of his state.
|
||||
This is why 'is_won' and 'decrement' parameters are used to describe the from / to change of its state.
|
||||
"""
|
||||
new_frequencies_by_team, existing_frequencies_by_team = self._pls_prepare_update_frequency_table(target_state=from_state or to_state)
|
||||
|
||||
|
||||
@@ -408,7 +408,7 @@ class TestLeadMerge(TestLeadMergeCommon):
|
||||
lead_w_email contact_1 KO (already following the destination lead)
|
||||
contact_2 OK (active on lead_w_email)
|
||||
contact_company KO (most recent message on lead_w_email is 35 days ago, message
|
||||
on lead_w_partner is not counted as he doesn't follow it)
|
||||
on lead_w_partner is not counted as they don't follow it)
|
||||
lead_w_partner contact_2 KO (already added with lead_w_email)
|
||||
lead_w_partner_company
|
||||
"""
|
||||
@@ -480,7 +480,7 @@ class TestLeadMerge(TestLeadMergeCommon):
|
||||
self.assertIn(self.contact_2, new_partner_followers,
|
||||
'The partner must follow the destination lead')
|
||||
# "contact_company" posted a message 35 days ago on lead_2, so it's considered as inactive
|
||||
# "contact_company" posted a message now on lead_3, but he doesn't follow lead_3
|
||||
# "contact_company" posted a message now on lead_3, but they don't follow lead_3
|
||||
# so this message is just ignored
|
||||
self.assertNotIn(self.contact_company, new_partner_followers,
|
||||
'The partner was not active on the lead')
|
||||
|
||||
@@ -90,7 +90,7 @@ class TestCRMLeadMultiCompany(TestCrmCommon):
|
||||
@users('user_sales_manager_mc')
|
||||
def test_lead_mc_company_computation_env_user_restrict(self):
|
||||
""" Check that the computed company is allowed (aka in self.env.companies).
|
||||
User is logged in company_main even his default default company is
|
||||
User is logged in company_main even their default default company is
|
||||
company_2. """
|
||||
LeadUnsyncCids = self.env['crm.lead'].with_context(allowed_company_ids=[self.company_main.id])
|
||||
self.assertEqual(LeadUnsyncCids.env.company, self.company_main)
|
||||
|
||||
@@ -12,7 +12,7 @@ class TestUi(HttpCase):
|
||||
self.start_tour("/web", 'crm_tour', login="admin")
|
||||
|
||||
def test_02_crm_tour_rainbowman(self):
|
||||
# we create a new user to make sure he gets the 'Congrats on your first deal!'
|
||||
# we create a new user to make sure they get the 'Congrats on your first deal!'
|
||||
# rainbowman message.
|
||||
self.env['res.users'].create({
|
||||
'name': 'Temporary CRM User',
|
||||
|
||||
@@ -227,7 +227,7 @@ class CRMLeadMiningRequest(models.Model):
|
||||
def _perform_request(self):
|
||||
"""
|
||||
This will perform the request and create the corresponding leads.
|
||||
The user will be notified if he hasn't enough credits.
|
||||
The user will be notified if they don't have enough credits.
|
||||
"""
|
||||
self.error_type = False
|
||||
server_payload = self._prepare_iap_payload()
|
||||
|
||||
@@ -43,7 +43,7 @@ class ChatbotScriptStep(models.Model):
|
||||
|
||||
The whole conversation history will be saved into the lead's description for reference.
|
||||
This also allows having a question of type 'free_input_multi' to let the visitor explain
|
||||
his interest / needs before creating the lead. """
|
||||
their interest / needs before creating the lead. """
|
||||
|
||||
customer_values = self._chatbot_prepare_customer_values(
|
||||
mail_channel, create_partner=False, update_partner=True)
|
||||
|
||||
@@ -61,7 +61,7 @@ class TestLivechatLead(TestCrmCommon):
|
||||
self.assertEqual(lead.name, 'TestLead command')
|
||||
self.assertEqual(lead.partner_id, self.env['res.partner'])
|
||||
|
||||
# public + someone else: no customer (as he was anonymous)
|
||||
# public + someone else: no customer (as they were anonymous)
|
||||
channel.write({
|
||||
'channel_partner_ids': [(4, self.user_sales_manager.partner_id.id)]
|
||||
})
|
||||
|
||||
@@ -53,7 +53,7 @@ class MailPluginController(mail_plugin.MailPluginController):
|
||||
|
||||
def _get_contact_data(self, partner):
|
||||
"""
|
||||
Return the leads key only if the current user can create leads. So, if he can not
|
||||
Return the leads key only if the current user can create leads. So, if they can not
|
||||
create leads, the section won't be visible on the addin side (like if the CRM
|
||||
module was not installed on the database).
|
||||
"""
|
||||
|
||||
@@ -6,7 +6,7 @@ from odoo import api, fields, models, _
|
||||
|
||||
class EventBoothRegistration(models.Model):
|
||||
"""event.booth.registrations are used to allow multiple partners to book the same booth.
|
||||
Whenever a partner has paid his registration all the others linked to the booth will be deleted."""
|
||||
Whenever a partner has paid their registration all the others linked to the booth will be deleted."""
|
||||
|
||||
_name = 'event.booth.registration'
|
||||
_description = 'Event Booth Registration'
|
||||
|
||||
@@ -47,7 +47,7 @@ class EventRegistration(models.Model):
|
||||
There are 2 main use cases
|
||||
|
||||
* first is when we update the partner_id of multiple registrations. It
|
||||
happens when a public user fill its information when he register to
|
||||
happens when a public user fill its information when they register to
|
||||
an event;
|
||||
* second is when we update specific values of one registration like
|
||||
updating question answers or a contact information (email, phone);
|
||||
|
||||
@@ -120,12 +120,12 @@ class GoogleEvent(abc.Set):
|
||||
def owner(self, env):
|
||||
# Owner/organizer could be desynchronised between Google and Odoo.
|
||||
# Let userA, userB be two new users (never synced to Google before).
|
||||
# UserA creates an event in Odoo (he is the owner) but userB syncs first.
|
||||
# UserA creates an event in Odoo (they are the owner) but userB syncs first.
|
||||
# There is no way to insert the event into userA's calendar since we don't have
|
||||
# any authentication access. The event is therefore inserted into userB's calendar
|
||||
# (he is the organizer in Google). The "real" owner (in Odoo) is stored as an
|
||||
# (they are the organizer in Google). The "real" owner (in Odoo) is stored as an
|
||||
# extended property. There is currently no support to "transfert" ownership when
|
||||
# userA syncs his calendar the first time.
|
||||
# userA syncs their calendar the first time.
|
||||
real_owner_id = self.extendedProperties and self.extendedProperties.get('shared', {}).get('%s_owner_id' % env.cr.dbname)
|
||||
try:
|
||||
# If we create an event without user_id, the event properties will be 'false'
|
||||
|
||||
@@ -95,7 +95,7 @@ class MailChannel(models.Model):
|
||||
channel_partner_ids = self.with_context(active_test=False).channel_partner_ids
|
||||
partners = channel_partner_ids - self.livechat_operator_id
|
||||
if not partners:
|
||||
# operator probably testing the livechat with his own user
|
||||
# operator probably testing the livechat with their own user
|
||||
partners = channel_partner_ids
|
||||
first_partner = partners and partners[0]
|
||||
if first_partner and not first_partner.is_public:
|
||||
|
||||
@@ -90,7 +90,7 @@ class TestGetMailChannel(TransactionCase):
|
||||
self.assertEqual(visitor_info['name'], "Roger")
|
||||
self.assertEqual(visitor_info['country'], (20, "Belgium"))
|
||||
|
||||
# ensure visitor info are correct when operator is testing himself
|
||||
# ensure visitor info are correct when operator is testing themselves
|
||||
operator = self.operators[0]
|
||||
channel_info = self.livechat_channel.with_user(operator)._open_livechat_mail_channel(anonymous_name='whatever', previous_operator_id=operator.partner_id.id, user_id=operator.id)
|
||||
self.assertEqual(channel_info['operator_pid'], (operator.partner_id.id, "Michel Operator"))
|
||||
|
||||
@@ -289,7 +289,7 @@ class PermissionPanel extends Component {
|
||||
/**
|
||||
* This method is called after each permission change rpc.
|
||||
* It will check if a reloading of the article tree or a complete reload is needed in function
|
||||
* of the new article state (if change of category or if user lost his own access to the current article).
|
||||
* of the new article state (if change of category or if user lost their own access to the current article).
|
||||
* return True if the caller should continue after executing this method, and False, if caller should stop.
|
||||
* @param {Dict} result
|
||||
* @param {Boolean} lostAccess
|
||||
|
||||
@@ -173,7 +173,7 @@ class TestKnowledgeSecurity(KnowledgeArticlePermissionsCase):
|
||||
self.article_roots.article_member_ids,
|
||||
'Members: employee should memberships of visible '
|
||||
)
|
||||
# remove employee from Shared root, check he cannot read those members
|
||||
# remove employee from Shared root, check they cannot read those members
|
||||
self.article_roots[2].article_member_ids.filtered(lambda m: m.partner_id == self.partner_employee).unlink()
|
||||
my_members = self.env['knowledge.article.member'].search([('article_id', 'in', self.article_roots.ids)])
|
||||
self.assertEqual(len(my_members), 2)
|
||||
|
||||
@@ -77,7 +77,7 @@ class MailController(http.Controller):
|
||||
record_sudo.with_user(uid).with_context(allowed_company_ids=cids).check_access_rule('read')
|
||||
except AccessError:
|
||||
# In case the allowed_company_ids from the cookies (i.e. the last user configuration
|
||||
# on his browser) is not sufficient to avoid an ir.rule access error, try to following
|
||||
# on their browser) is not sufficient to avoid an ir.rule access error, try to following
|
||||
# heuristic:
|
||||
# - Guess the supposed necessary company to access the record via the method
|
||||
# _get_mail_redirect_suggested_company
|
||||
|
||||
@@ -248,7 +248,7 @@ class Channel(models.Model):
|
||||
membership_pids = [cmd[2]['partner_id'] for cmd in membership_ids_cmd if cmd[0] == 0]
|
||||
|
||||
# always add current user to new channel to have right values for
|
||||
# is_pinned + ensure he has rights to see channel
|
||||
# is_pinned + ensure they have rights to see channel
|
||||
partner_ids_to_add = list(set(partner_ids + [self.env.user.partner_id.id]))
|
||||
vals['channel_last_seen_partner_ids'] = membership_ids_cmd + [
|
||||
(0, 0, {'partner_id': pid})
|
||||
@@ -659,7 +659,7 @@ class Channel(models.Model):
|
||||
|
||||
def _message_post_after_hook(self, message, msg_vals):
|
||||
"""
|
||||
Automatically set the message posted by the current user as seen for himself.
|
||||
Automatically set the message posted by the current user as seen for themselves.
|
||||
"""
|
||||
self._set_last_seen_message(message)
|
||||
return super()._message_post_after_hook(message=message, msg_vals=msg_vals)
|
||||
@@ -940,7 +940,7 @@ class Channel(models.Model):
|
||||
|
||||
def channel_fold(self, state=None):
|
||||
""" Update the fold_state of the given session. In order to syncronize web browser
|
||||
tabs, the change will be broadcast to himself (the current user channel).
|
||||
tabs, the change will be broadcast to themselves (the current user channel).
|
||||
Note: the user need to be logged
|
||||
:param state : the new status of the session for the current user.
|
||||
"""
|
||||
@@ -1252,7 +1252,7 @@ class Channel(models.Model):
|
||||
|
||||
def _execute_command_help_message_extra(self):
|
||||
msg = _("""<br><br>
|
||||
Type <b>@username</b> to mention someone, and grab his attention.<br>
|
||||
Type <b>@username</b> to mention someone, and grab their attention.<br>
|
||||
Type <b>#channel</b> to mention a channel.<br>
|
||||
Type <b>/command</b> to execute a command.<br>""")
|
||||
return msg
|
||||
|
||||
@@ -757,7 +757,7 @@ class Message(models.Model):
|
||||
""" Toggle messages as (un)starred. Technically, the notifications related
|
||||
to uid are set to (un)starred.
|
||||
"""
|
||||
# a user should always be able to star a message he can read
|
||||
# a user should always be able to star a message they can read
|
||||
self.check_access_rule('read')
|
||||
starred = not self.starred
|
||||
if starred:
|
||||
@@ -1004,7 +1004,7 @@ class Message(models.Model):
|
||||
messages = self.env['mail.message']
|
||||
for message in self:
|
||||
# Check if user has access to the record before displaying a notification about it.
|
||||
# In case the user switches from one company to another, it might happen that he doesn't
|
||||
# In case the user switches from one company to another, it might happen that they don't
|
||||
# have access to the record related to the notification. In this case, we skip it.
|
||||
# YTI FIXME: check allowed_company_ids if necessary
|
||||
if message.model and message.res_id:
|
||||
|
||||
@@ -1009,7 +1009,7 @@ class MailThread(models.AbstractModel):
|
||||
subtype_id = thread._creation_subtype().id
|
||||
|
||||
# replies to internal message are considered as notes, but parent message
|
||||
# author is added in recipients to ensure he is notified of a private answer
|
||||
# author is added in recipients to ensure they are notified of a private answer
|
||||
parent_message = False
|
||||
if message_dict.get('parent_id'):
|
||||
parent_message = self.env['mail.message'].sudo().browse(message_dict['parent_id'])
|
||||
@@ -2412,7 +2412,7 @@ class MailThread(models.AbstractModel):
|
||||
msg_vals = {}
|
||||
|
||||
# compute send user and its related signature; try to use self.env.user instead of browsing
|
||||
# user_ids if he is the author will give a sudo user, improving access performances and cache usage.
|
||||
# user_ids if they are the author will give a sudo user, improving access performances and cache usage.
|
||||
signature = ''
|
||||
email_add_signature = msg_vals.get('email_add_signature') if msg_vals and 'email_add_signature' in msg_vals else message.email_add_signature
|
||||
if email_add_signature:
|
||||
@@ -2824,7 +2824,7 @@ class MailThread(models.AbstractModel):
|
||||
documents. This is done using relational fields linking to res.users
|
||||
with track_visibility set. Since OpenERP v7 it is considered as being
|
||||
responsible for the document and therefore standard behavior is to
|
||||
subscribe the user and send him a notification.
|
||||
subscribe the user and send them a notification.
|
||||
|
||||
Override this method to change that behavior and/or to add people to
|
||||
notify, using possible custom notification.
|
||||
|
||||
@@ -45,7 +45,7 @@ FieldChar.include({
|
||||
/**
|
||||
* Triggers the 'change' event to refresh the value.
|
||||
* This method is debounced to run 2 seconds after typing ends.
|
||||
* (to avoid spamming the server while the user is typing his message)
|
||||
* (to avoid spamming the server while the user is typing their message)
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
@@ -62,7 +62,7 @@ var FieldEmojiCommon = {
|
||||
* By default, the 'change' event is only triggered when the text element is blurred.
|
||||
*
|
||||
* We override this method because we want to update the value while
|
||||
* the user is typing his message (and not only on blur).
|
||||
* the user is typing their message (and not only on blur).
|
||||
*
|
||||
* @override
|
||||
* @private
|
||||
@@ -90,7 +90,7 @@ var FieldEmojiCommon = {
|
||||
/**
|
||||
* Triggers the 'change' event to refresh the value.
|
||||
* This method is debounced to run 2 seconds after typing ends.
|
||||
* (to avoid spamming the server while the user is typing his message)
|
||||
* (to avoid spamming the server while the user is typing their message)
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
@@ -193,7 +193,7 @@ registerModel({
|
||||
/**
|
||||
* States the counter of this messaging menu. The counter is an integer
|
||||
* value to give to the current user an estimate of how many things
|
||||
* (unread threads, notifications, ...) are yet to be processed by him.
|
||||
* (unread threads, notifications, ...) are yet to be processed by them.
|
||||
*/
|
||||
counter: attr({
|
||||
compute: '_computeCounter',
|
||||
|
||||
@@ -1231,7 +1231,7 @@ registerModel({
|
||||
inverse: 'rtcAsConnectedSession',
|
||||
}),
|
||||
/**
|
||||
* String, peerToken of the current session used to identify him during the peer-to-peer transactions.
|
||||
* String, peerToken of the current session used to identify them during the peer-to-peer transactions.
|
||||
*/
|
||||
currentRtcSession: one('RtcSession', {
|
||||
inverse: 'rtcAsCurrentSession',
|
||||
|
||||
@@ -30,7 +30,7 @@ registerModel({
|
||||
recordMethods: {
|
||||
/**
|
||||
* Shows the dropzone when entering the browser window, to let the user know
|
||||
* where he can drop its file.
|
||||
* where they can drop their file.
|
||||
* Avoids changing state when entering inner dropzones.
|
||||
*
|
||||
* @private
|
||||
|
||||
@@ -789,7 +789,7 @@ QUnit.test('data-oe-id & data-oe-model link redirection on click', async functio
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('chat with author should be opened after clicking on his avatar', async function (assert) {
|
||||
QUnit.test('chat with author should be opened after clicking on their avatar', async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
@@ -821,11 +821,11 @@ QUnit.test('chat with author should be opened after clicking on his avatar', asy
|
||||
assert.strictEqual(
|
||||
document.querySelector('.o_ChatWindow_thread').dataset.correspondentId,
|
||||
message.author.id.toString(),
|
||||
"chat with author should be opened after clicking on his avatar"
|
||||
"chat with author should be opened after clicking on their avatar"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('chat with author should be opened after clicking on his im status icon', async function (assert) {
|
||||
QUnit.test('chat with author should be opened after clicking on their im status icon', async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
@@ -857,7 +857,7 @@ QUnit.test('chat with author should be opened after clicking on his im status ic
|
||||
assert.strictEqual(
|
||||
document.querySelector('.o_ChatWindow_thread').dataset.correspondentId,
|
||||
message.author.id.toString(),
|
||||
"chat with author should be opened after clicking on his im status icon"
|
||||
"chat with author should be opened after clicking on their im status icon"
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ class MockEmail(common.BaseCase, MockSmtplibCase):
|
||||
def gateway_mail_reply_wrecord(self, template, record, use_in_reply_to=True,
|
||||
target_model=None, target_field=None):
|
||||
""" Simulate a reply through the mail gateway. Usage: giving a record,
|
||||
find an email sent to him and use its message-ID to simulate a reply.
|
||||
find an email sent to them and use its message-ID to simulate a reply.
|
||||
|
||||
Some noise is added in References just to test some robustness. """
|
||||
mail_mail = self._find_mail_mail_wrecord(record)
|
||||
@@ -168,7 +168,7 @@ class MockEmail(common.BaseCase, MockSmtplibCase):
|
||||
def gateway_mail_reply_wemail(self, template, email_to, use_in_reply_to=True,
|
||||
target_model=None, target_field=None):
|
||||
""" Simulate a reply through the mail gateway. Usage: giving a record,
|
||||
find an email sent to him and use its message-ID to simulate a reply.
|
||||
find an email sent to them and use its message-ID to simulate a reply.
|
||||
|
||||
Some noise is added in References just to test some robustness. """
|
||||
sent_mail = self._find_sent_mail_wemail(email_to)
|
||||
|
||||
@@ -118,14 +118,14 @@ class TestMailChannelMembers(MailCommon):
|
||||
channel_partners = self.env['mail.channel.partner'].search([('channel_id', '=', self.private_channel.id)])
|
||||
self.assertEqual(len(channel_partners), 1)
|
||||
|
||||
# User 2 is not in the private channel, he can not invite user 3
|
||||
# User 2 is not in the private channel, they can not invite user 3
|
||||
with self.assertRaises(AccessError):
|
||||
self.env['mail.channel.partner'].with_user(self.user_2).create({
|
||||
'partner_id': self.user_portal.partner_id.id,
|
||||
'channel_id': self.private_channel.id,
|
||||
})
|
||||
|
||||
# User 1 is in the private channel, he can invite other users
|
||||
# User 1 is in the private channel, they can invite other users
|
||||
self.env['mail.channel.partner'].with_user(self.user_1).create({
|
||||
'partner_id': self.user_portal.partner_id.id,
|
||||
'channel_id': self.private_channel.id,
|
||||
@@ -147,13 +147,13 @@ class TestMailChannelMembers(MailCommon):
|
||||
channel_partners = self.env['mail.channel.partner'].search([('channel_id', '=', self.private_channel.id)])
|
||||
self.assertEqual(channel_partners.mapped('partner_id'), self.user_1.partner_id)
|
||||
|
||||
# User 2 is not in the channel, he can not invite user_portal
|
||||
# User 2 is not in the channel, they can not invite user_portal
|
||||
with self.assertRaises(AccessError):
|
||||
self.private_channel.with_user(self.user_2).add_members(self.user_portal.partner_id.ids)
|
||||
channel_partners = self.env['mail.channel.partner'].search([('channel_id', '=', self.private_channel.id)])
|
||||
self.assertEqual(channel_partners.mapped('partner_id'), self.user_1.partner_id)
|
||||
|
||||
# User 1 is in the channel, he can invite user_portal
|
||||
# User 1 is in the channel, they can invite user_portal
|
||||
self.private_channel.with_user(self.user_1).add_members(self.user_portal.partner_id.ids)
|
||||
channel_partners = self.env['mail.channel.partner'].search([('channel_id', '=', self.private_channel.id)])
|
||||
self.assertEqual(channel_partners.mapped('partner_id'), self.user_1.partner_id | self.user_portal.partner_id)
|
||||
@@ -165,11 +165,11 @@ class TestMailChannelMembers(MailCommon):
|
||||
channel_partners = self.env['mail.channel.partner'].search([('channel_id', '=', self.private_channel.id)])
|
||||
self.assertEqual(len(channel_partners), 2)
|
||||
|
||||
# User 2 is not in the channel, he can not kick user 1
|
||||
# User 2 is not in the channel, they can not kick user 1
|
||||
with self.assertRaises(AccessError):
|
||||
channel_partners.with_user(self.user_2).unlink()
|
||||
|
||||
# User 3 is in the channel, he can kick user 1
|
||||
# User 3 is in the channel, they can kick user 1
|
||||
channel_partners.with_user(self.user_portal).unlink()
|
||||
|
||||
# ------------------------------------------------------------
|
||||
@@ -181,12 +181,12 @@ class TestMailChannelMembers(MailCommon):
|
||||
channel_partners = self.env['mail.channel.partner'].search([('channel_id', '=', self.group_channel.id)])
|
||||
self.assertFalse(channel_partners)
|
||||
|
||||
# user 1 is in the group, he can join the channel
|
||||
# user 1 is in the group, they can join the channel
|
||||
self.group_channel.with_user(self.user_1).add_members(self.user_1.partner_id.ids)
|
||||
channel_partners = self.env['mail.channel.partner'].search([('channel_id', '=', self.group_channel.id)])
|
||||
self.assertEqual(channel_partners.mapped('partner_id'), self.user_1.partner_id)
|
||||
|
||||
# user 3 is not in the group, he can not join
|
||||
# user 3 is not in the group, they can not join
|
||||
with self.assertRaises(AccessError):
|
||||
self.group_channel.with_user(self.user_portal).add_members(self.user_portal.partner_id.ids)
|
||||
|
||||
@@ -197,7 +197,7 @@ class TestMailChannelMembers(MailCommon):
|
||||
channel_partners = self.env['mail.channel.partner'].search([('channel_id', '=', self.group_channel.id)])
|
||||
self.assertEqual(channel_partners.mapped('partner_id'), self.user_1.partner_id)
|
||||
|
||||
# user 1 can not invite user 3 because he's not in the group
|
||||
# user 1 can not invite user 3 because they are not in the group
|
||||
with self.assertRaises(UserError):
|
||||
self.group_channel.with_user(self.user_1).add_members(self.user_portal.partner_id.ids)
|
||||
channel_partners = self.env['mail.channel.partner'].search([('channel_id', '=', self.group_channel.id)])
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- Disable odoobot on admin so that devs don't hate him -->
|
||||
<!-- Disable odoobot on admin so that devs don't hate it -->
|
||||
<record id="base.user_admin" model="res.users">
|
||||
<field name="odoobot_state">disabled</field>
|
||||
</record>
|
||||
|
||||
@@ -418,7 +418,7 @@ class MailGroup(models.Model):
|
||||
for batch_email_member in tools.split_every(GROUP_SEND_BATCH_SIZE, member_emails.items()):
|
||||
for email_member_normalized, email_member in batch_email_member:
|
||||
if email_member_normalized == message.email_from_normalized:
|
||||
# Do not send the email to his author
|
||||
# Do not send the email to their author
|
||||
continue
|
||||
|
||||
# SMTP headers related to the subscription
|
||||
|
||||
@@ -214,7 +214,7 @@ class TestMailGroup(TestMailListCommon):
|
||||
})]})
|
||||
self.assertIn(partner, mail_group.member_partner_ids)
|
||||
|
||||
# Now that portal is in the member list he should have access
|
||||
# Now that portal is in the member list they should have access
|
||||
mail_group.with_user(self.user_employee_2).check_access_rule('read')
|
||||
with self.assertRaises(AccessError, msg='Only moderator / responsible and admin can write on the group'):
|
||||
mail_group.with_user(self.user_employee_2).check_access_rule('write')
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
Reject the message<span attrs="{'invisible': [('send_email', '=', False)]}"> and send an email to the author (<field name="email_from_normalized"/>)</span>.
|
||||
</div>
|
||||
<div class="alert alert-warning" role="alert" attrs="{'invisible': [('action', '!=', 'ban')]}">
|
||||
Ban the author of the message (<field name="email_from_normalized"/>) <span attrs="{'invisible': [('send_email', '=', False)]}">and send him an email</span>.
|
||||
Ban the author of the message (<field name="email_from_normalized"/>) <span attrs="{'invisible': [('send_email', '=', False)]}">and send them an email</span>.
|
||||
</div>
|
||||
<group>
|
||||
<field name="send_email" invisible="1"/>
|
||||
|
||||
@@ -32,7 +32,7 @@ class Authenticate(http.Controller):
|
||||
def auth_confirm(self, scope, friendlyname, redirect, info=None, do=None, **kw):
|
||||
"""
|
||||
Called by the `app_auth` template. If the user decided to allow the app to access Odoo, a temporary auth code
|
||||
is generated and he is redirected to `redirect` with this code in the URL. It should redirect to the app, and
|
||||
is generated and they are redirected to `redirect` with this code in the URL. It should redirect to the app, and
|
||||
the app should then exchange this auth code for an access token by calling
|
||||
`/mail_client/auth/access_token`.
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ class MassMailController(http.Controller):
|
||||
raise exceptions.AccessDenied()
|
||||
|
||||
if mailing.mailing_model_real == 'mailing.contact':
|
||||
# Unsubscribe directly + Let the user choose his subscriptions
|
||||
# Unsubscribe directly + Let the user choose their subscriptions
|
||||
mailing.update_opt_out(email, mailing.contact_list_ids.ids, True)
|
||||
|
||||
contacts = request.env['mailing.contact'].sudo().search([('email_normalized', '=', tools.email_normalize(email))])
|
||||
|
||||
@@ -12,7 +12,7 @@ class MassMailingList(models.Model):
|
||||
_description = 'Mailing List'
|
||||
_mailing_enabled = True
|
||||
_order = 'create_date DESC'
|
||||
# As this model has his own data merge, avoid to enable the generic data_merge on that model.
|
||||
# As this model has their own data merge, avoid to enable the generic data_merge on that model.
|
||||
_disable_data_merge = True
|
||||
|
||||
name = fields.Char(string='Mailing List', required=True)
|
||||
@@ -34,7 +34,7 @@ class MassMailingList(models.Model):
|
||||
copy=True, depends=['contact_ids'])
|
||||
is_public = fields.Boolean(default=True, string='Show In Preferences',
|
||||
help='The mailing list can be accessible by recipient in the unsubscription'
|
||||
' page to allows him to update his subscription preferences.')
|
||||
' page to allows them to update their subscription preferences.')
|
||||
|
||||
# ------------------------------------------------------
|
||||
# COMPUTE / ONCHANGE
|
||||
|
||||
@@ -13,7 +13,7 @@ class ResConfigSettings(models.TransientModel):
|
||||
mass_mailing_mail_server_id = fields.Many2one('ir.mail_server', string='Mail Server', config_parameter='mass_mailing.mail_server_id')
|
||||
show_blacklist_buttons = fields.Boolean(string="Blacklist Option when Unsubscribing",
|
||||
config_parameter='mass_mailing.show_blacklist_buttons',
|
||||
help="""Allow the recipient to manage himself his state in the blacklist via the unsubscription page.""")
|
||||
help="""Allow the recipient to manage themselves their state in the blacklist via the unsubscription page.""")
|
||||
mass_mailing_reports = fields.Boolean(string='24H Stat Mailing Reports', config_parameter='mass_mailing.mass_mailing_reports',
|
||||
help='Check how well your mailing is doing a day after it has been sent.')
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 o_setting_box col-xs-12" name="allow_blacklist_setting_container">
|
||||
<div class="o_setting_left_pane" title="Allow the recipient to manage himself his state in the blacklist via the unsubscription page.
|
||||
<div class="o_setting_left_pane" title="Allow the recipient to manage themselves their state in the blacklist via the unsubscription page.
|
||||
If the option is active, the 'Blacklist Me' button is hidden on the unsubscription page.
|
||||
The 'come Back' button will always be visible in any case to allow leads and partners to re-subscribe.">
|
||||
<field name="show_blacklist_buttons"/>
|
||||
|
||||
@@ -109,12 +109,12 @@ class MicrosoftEvent(abc.Set):
|
||||
def owner(self, env):
|
||||
# Owner/organizer could be desynchronised between Microsoft and Odoo.
|
||||
# Let userA, userB be two new users (never synced to Microsoft before).
|
||||
# UserA creates an event in Odoo (he is the owner) but userB syncs first.
|
||||
# UserA creates an event in Odoo (they are the owner) but userB syncs first.
|
||||
# There is no way to insert the event into userA's calendar since we don't have
|
||||
# any authentication access. The event is therefore inserted into userB's calendar
|
||||
# (he is the orginizer in Microsoft). The "real" owner (in Odoo) is stored as an
|
||||
# (they are the organizer in Microsoft). The "real" owner (in Odoo) is stored as an
|
||||
# extended property. There is currently no support to "transfert" ownership when
|
||||
# userA syncs his calendar the first time.
|
||||
# userA syncs their calendar the first time.
|
||||
if self.singleValueExtendedProperties:
|
||||
microsoft_guid = env['ir.config_parameter'].sudo().get_param('microsoft_calendar.microsoft_guid', False)
|
||||
real_owner_id = [prop['value'] for prop in self.singleValueExtendedProperties if prop['id'] == 'String {%s} Name owner_odoo_id' % microsoft_guid][0]
|
||||
|
||||
@@ -218,7 +218,7 @@ class MailController(mail.MailController):
|
||||
@classmethod
|
||||
def _redirect_to_record(cls, model, res_id, access_token=None, **kwargs):
|
||||
""" If the current user doesn't have access to the document, but provided
|
||||
a valid access token, redirect him to the front-end view.
|
||||
a valid access token, redirect them to the front-end view.
|
||||
If the partner_id and hash parameters are given, add those parameters to the redirect url
|
||||
to authentify the recipient in the chatter, if any.
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ class MailPluginController(mail_plugin.MailPluginController):
|
||||
This is structured this way to enable the "project" feature on the Outlook side only if the Odoo version
|
||||
supports it.
|
||||
|
||||
Return the tasks key only if the current user can create tasks. So, if he can not
|
||||
Return the tasks key only if the current user can create tasks. So, if they can not
|
||||
create tasks, the section won't be visible on the addin side (like if the project
|
||||
module was not installed on the database).
|
||||
"""
|
||||
|
||||
@@ -84,8 +84,8 @@ class UserInputSession(http.Controller):
|
||||
Frontend should take the delay into account by displaying the appropriate animations.
|
||||
|
||||
Writing the next question on the survey is sudo'ed to avoid potential access right issues.
|
||||
e.g: a survey user can create a live session from any survey but he can only write
|
||||
on its own survey.
|
||||
e.g: a survey user can create a live session from any survey but they can only write
|
||||
on their own survey.
|
||||
|
||||
In addition to return a pre-rendered html template with the next question, we also return the background
|
||||
to display. Background image depends on the next question to display and cannot be extracted from the
|
||||
|
||||
@@ -126,7 +126,7 @@ class Survey(models.Model):
|
||||
certification_mail_template_id = fields.Many2one(
|
||||
'mail.template', 'Certified Email Template',
|
||||
domain="[('model', '=', 'survey.user_input')]",
|
||||
help="Automated email sent to the user when he succeeds the certification, containing his certification document.")
|
||||
help="Automated email sent to the user when they succeed the certification, containing their certification document.")
|
||||
certification_report_layout = fields.Selection([
|
||||
('modern_purple', 'Modern Purple'),
|
||||
('modern_blue', 'Modern Blue'),
|
||||
@@ -993,7 +993,7 @@ class Survey(models.Model):
|
||||
def action_start_session(self):
|
||||
""" Sets the necessary fields for the session to take place and starts it.
|
||||
The write is sudo'ed because a survey user can start a session even if it's
|
||||
not his own survey. """
|
||||
not their own survey. """
|
||||
|
||||
if not self.env.user.has_group('survey.group_survey_user'):
|
||||
raise AccessError(_('Only survey users can manage sessions.'))
|
||||
@@ -1019,7 +1019,7 @@ class Survey(models.Model):
|
||||
|
||||
def action_end_session(self):
|
||||
""" The write is sudo'ed because a survey user can end a session even if it's
|
||||
not his own survey. """
|
||||
not their own survey. """
|
||||
|
||||
if not self.env.user.has_group('survey.group_survey_user'):
|
||||
raise AccessError(_('Only survey users can manage sessions.'))
|
||||
|
||||
@@ -45,7 +45,7 @@ class SurveyUserInput(models.Model):
|
||||
invite_token = fields.Char('Invite token', readonly=True, copy=False) # no unique constraint, as it identifies a pool of attempts
|
||||
partner_id = fields.Many2one('res.partner', string='Contact', readonly=True)
|
||||
email = fields.Char('Email', readonly=True)
|
||||
nickname = fields.Char('Nickname', help="Attendee nickname, mainly used to identify him in the survey session leaderboard.")
|
||||
nickname = fields.Char('Nickname', help="Attendee nickname, mainly used to identify them in the survey session leaderboard.")
|
||||
# questions / answers
|
||||
user_input_line_ids = fields.One2many('survey.user_input.line', 'user_input_id', string='Answers', copy=True)
|
||||
predefined_question_ids = fields.Many2many('survey.question', string='Predefined Questions', readonly=True)
|
||||
@@ -554,11 +554,11 @@ class SurveyUserInput(models.Model):
|
||||
- ensure correct scoring
|
||||
- if the selected answer triggers another question later in the survey, if the answer is not cleared,
|
||||
a question that should not be displayed to the user will be.
|
||||
|
||||
TODO DBE: Maybe this can be the only cleaning method, even for section_per_page or one_page where
|
||||
conditional questions are, for now, cleared in JS directly. But this can be annoying if user typed a long
|
||||
answer, changed his mind unchecking depending answer and changed again his mind by rechecking the depending
|
||||
answer -> For now, the long answer will be lost. If we use this as the master cleaning method,
|
||||
|
||||
TODO DBE: Maybe this can be the only cleaning method, even for section_per_page or one_page where
|
||||
conditional questions are, for now, cleared in JS directly. But this can be annoying if user typed a long
|
||||
answer, changed their mind unchecking depending answer and changed again their mind by rechecking the depending
|
||||
answer -> For now, the long answer will be lost. If we use this as the master cleaning method,
|
||||
long answer will be cleared only during submit.
|
||||
"""
|
||||
inactive_questions = self._get_inactive_conditional_questions()
|
||||
|
||||
@@ -98,7 +98,7 @@ publicWidget.registry.SurveySessionChart = publicWidget.Widget.extend({
|
||||
* (see _getBackgroundColor for details)
|
||||
* - The ticks are bigger and bolded to be able to see them better on a big screen (projector)
|
||||
* - We don't use tooltips to keep it as simple as possible
|
||||
* - We don't set a suggestedMin or Max so that Chart will adapt automatically himself based on the given data
|
||||
* - We don't set a suggestedMin or Max so that Chart will adapt automatically based on the given data
|
||||
* The '+1' part is a small trick to avoid the datalabels to be clipped in height
|
||||
* - We use a custom 'datalabels' plugin to be able to display the number value on top of the
|
||||
* associated bar of the chart.
|
||||
|
||||
@@ -49,7 +49,7 @@ class TestActivityRights(TestActivityCommon):
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
def test_activity_security_user_noaccess_automated(self):
|
||||
def _employee_crash(*args, **kwargs):
|
||||
""" If employee is test employee, consider he has no access on document """
|
||||
""" If employee is test employee, consider they have no access on document """
|
||||
recordset = args[0]
|
||||
if recordset.env.uid == self.user_employee.id:
|
||||
raise exceptions.AccessError('Hop hop hop Ernest, please step back.')
|
||||
@@ -65,7 +65,7 @@ class TestActivityRights(TestActivityCommon):
|
||||
|
||||
def test_activity_security_user_noaccess_manual(self):
|
||||
def _employee_crash(*args, **kwargs):
|
||||
""" If employee is test employee, consider he has no access on document """
|
||||
""" If employee is test employee, consider they have no access on document """
|
||||
recordset = args[0]
|
||||
if recordset.env.uid == self.user_employee.id:
|
||||
raise exceptions.AccessError('Hop hop hop Ernest, please step back.')
|
||||
|
||||
@@ -604,7 +604,7 @@ class TestMailgateway(TestMailCommon):
|
||||
@mute_logger('odoo.addons.mail.models.mail_thread')
|
||||
def test_message_process_create_uid_crash(self):
|
||||
def _employee_crash(*args, **kwargs):
|
||||
""" If employee is test employee, consider he has no access on document """
|
||||
""" If employee is test employee, consider they have no access on document """
|
||||
recordset = args[0]
|
||||
if recordset.env.uid == self.user_employee.id and not recordset.env.su:
|
||||
if kwargs.get('raise_exception', True):
|
||||
|
||||
@@ -363,7 +363,7 @@ class TestMessageAccess(TestMailCommon):
|
||||
self.message.write({'attachment_ids': [(4, attachment.id)]})
|
||||
self.message.write({'partner_ids': [(4, self.user_employee.partner_id.id)]})
|
||||
self.message.with_user(self.user_employee).read()
|
||||
# Test: Bert has access to attachment, ok because he can read message
|
||||
# Test: Bert has access to attachment, ok because they can read message
|
||||
attachment.with_user(self.user_employee).read(['name', 'datas'])
|
||||
|
||||
def test_mail_message_access_read_author(self):
|
||||
@@ -372,7 +372,7 @@ class TestMessageAccess(TestMailCommon):
|
||||
|
||||
def test_mail_message_access_read_doc(self):
|
||||
self.message.write({'model': 'mail.channel', 'res_id': self.group_public.id})
|
||||
# Test: Bert reads the message, ok because linked to a doc he is allowed to read
|
||||
# Test: Bert reads the message, ok because linked to a doc they are allowed to read
|
||||
self.message.with_user(self.user_employee).read()
|
||||
|
||||
# --------------------------------------------------
|
||||
|
||||
@@ -117,7 +117,7 @@ class TestDiscuss(TestMailCommon, TestRecipients):
|
||||
@mute_logger('openerp.addons.mail.models.mail_mail')
|
||||
def test_mark_all_as_read(self):
|
||||
def _employee_crash(*args, **kwargs):
|
||||
""" If employee is test employee, consider he has no access on document """
|
||||
""" If employee is test employee, consider they have no access on document """
|
||||
recordset = args[0]
|
||||
if recordset.env.uid == self.user_employee.id and not recordset.env.su:
|
||||
if kwargs.get('raise_exception', True):
|
||||
|
||||
@@ -359,7 +359,7 @@ class TestTrackingInternals(TestMailCommon):
|
||||
self.record.clear_caches()
|
||||
record_form = Form(self.record.with_user(self.user_employee))
|
||||
record_form.name = 'TestDoNoCrash'
|
||||
# the employee user must be able to save the fields on which he can write
|
||||
# the employee user must be able to save the fields on which they can write
|
||||
# if we fetch all the tracked fields, ignoring the group of the current user
|
||||
# it will crash and it shouldn't
|
||||
record = record_form.save()
|
||||
|
||||
@@ -11,12 +11,12 @@ const tourUtils = require('website_sale.tour_utils');
|
||||
* -> clicks on "buy course"
|
||||
* -> is redirected to webshop on the product page
|
||||
* -> buys the course
|
||||
* -> fails 3 times, exhausting his attempts
|
||||
* -> fails 3 times, exhausting their attempts
|
||||
* -> is removed to the members of the course
|
||||
* -> buys the course again
|
||||
* -> succeeds the certification
|
||||
* -> has the course marked as completed
|
||||
* -> has the certification in his user profile
|
||||
* -> has the certification in their user profile
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
@@ -62,6 +62,6 @@ class QuizAnswer(models.Model):
|
||||
is_correct = fields.Boolean('Correct', default=False)
|
||||
comment = fields.Text(
|
||||
'Extra Comment', translate=True,
|
||||
help='''This comment will be displayed to the user if he selects this answer, after submitting the quiz.
|
||||
help='''This comment will be displayed to the user if they select this answer, after submitting the quiz.
|
||||
It is used as a small informational text helping to understand why this answer is correct / incorrect.''')
|
||||
awarded_points = fields.Integer('Points', default=0)
|
||||
|
||||
@@ -68,7 +68,7 @@ var Quiz = publicWidget.Widget.extend({
|
||||
* Overridden to add custom rendering behavior upon start of the widget.
|
||||
*
|
||||
* If the user has answered the quiz before having joined the course, we check
|
||||
* his answers (saved into his session) here as well.
|
||||
* their answers (saved into their session) here as well.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
|
||||
@@ -13,7 +13,7 @@ class MailChannel(models.Model):
|
||||
def channel_pin(self, pinned=False):
|
||||
""" Override to clean an empty livechat channel.
|
||||
This is typically called when the operator send a chat request to a website.visitor
|
||||
but don't speak to him and closes the chatter.
|
||||
but don't speak to them and closes the chatter.
|
||||
This allows operators to send the visitor a new chat request.
|
||||
If active empty livechat channel,
|
||||
delete mail_channel as not useful to keep empty chat
|
||||
|
||||
@@ -58,7 +58,7 @@ class ResPartner(models.Model):
|
||||
|
||||
def action_view_courses(self):
|
||||
""" View partners courses. In singleton mode, return courses followed
|
||||
by all its contacts (if company) or by himself (if not a company).
|
||||
by all its contacts (if company) or by themselves (if not a company).
|
||||
Otherwise simply set a domain on required partners. """
|
||||
action = self.env["ir.actions.actions"]._for_xml_id("website_slides.slide_channel_partner_action")
|
||||
action['name'] = _('Followed Courses')
|
||||
|
||||
@@ -105,7 +105,7 @@ class ChannelUsersRelation(models.Model):
|
||||
users.add_karma(partner_karma[user.partner_id.id])
|
||||
|
||||
def _send_completed_mail(self):
|
||||
""" Send an email to the attendee when he has successfully completed a course. """
|
||||
""" Send an email to the attendee when they have successfully completed a course. """
|
||||
template_to_records = dict()
|
||||
for record in self:
|
||||
template = record.channel_id.completed_template_id
|
||||
@@ -403,7 +403,7 @@ class Channel(models.Model):
|
||||
@api.depends_context('uid')
|
||||
def _compute_can_publish(self):
|
||||
""" For channels of type 'training', only the responsible (see user_id field) can publish slides.
|
||||
The 'sudo' user needs to be handled because he's the one used for uploads done on the front-end when the
|
||||
The 'sudo' user needs to be handled because they are the one used for uploads done on the front-end when the
|
||||
logged in user is not publisher but fulfills the upload_group_ids condition. """
|
||||
for record in self:
|
||||
if not record.can_upload:
|
||||
@@ -481,7 +481,7 @@ class Channel(models.Model):
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for vals in vals_list:
|
||||
# Ensure creator is member of its channel it is easier for him to manage it (unless it is odoobot)
|
||||
# Ensure creator is member of its channel it is easier for them to manage it (unless it is odoobot)
|
||||
if not vals.get('channel_partner_ids') and not self.env.is_superuser():
|
||||
vals['channel_partner_ids'] = [(0, 0, {
|
||||
'partner_id': self.env.user.partner_id.id
|
||||
|
||||
@@ -56,4 +56,4 @@ class SlideAnswer(models.Model):
|
||||
question_id = fields.Many2one('slide.question', string="Question", required=True, ondelete='cascade')
|
||||
text_value = fields.Char("Answer", required=True, translate=True)
|
||||
is_correct = fields.Boolean("Is correct answer")
|
||||
comment = fields.Text("Comment", translate=True, help='This comment will be displayed to the user if he selects this answer')
|
||||
comment = fields.Text("Comment", translate=True, help='This comment will be displayed to the user if they select this answer')
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
* Overridden to add custom rendering behavior upon start of the widget.
|
||||
*
|
||||
* If the user has answered the quiz before having joined the course, we check
|
||||
* his answers (saved into his session) here as well.
|
||||
* their answers (saved into their session) here as well.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
|
||||
@@ -6,9 +6,9 @@ import slidesTourTools from '@website_slides/../tests/tours/slides_tour_tools';
|
||||
/**
|
||||
* Global use case:
|
||||
* a user (website publisher) creates a course;
|
||||
* he updates it;
|
||||
* he creates some lessons in it;
|
||||
* he publishes it;
|
||||
* they update it;
|
||||
* they create some lessons in it;
|
||||
* they publish it;
|
||||
*/
|
||||
tour.register('course_publisher_standard', {
|
||||
url: '/slides',
|
||||
|
||||
@@ -6,10 +6,10 @@ import tour from 'web_tour.tour';
|
||||
* Global use case:
|
||||
* an user (either employee, website publisher or portal) joins a public
|
||||
course;
|
||||
* he has access to the full course content when he's a member of the
|
||||
* they have access to the full course content when they are a member of the
|
||||
course;
|
||||
* he uses fullscreen player to complete the course;
|
||||
* he rates the course;
|
||||
* they use fullscreen player to complete the course;
|
||||
* they rate the course;
|
||||
*/
|
||||
tour.register('course_member', {
|
||||
url: '/slides',
|
||||
|
||||
@@ -23,10 +23,10 @@ FullScreen.include({
|
||||
* Global use case:
|
||||
* an user (either employee, website publisher or portal) joins a public
|
||||
course;
|
||||
* he has access to the full course content when he's a member of the
|
||||
* they have access to the full course content when they are a member of the
|
||||
course;
|
||||
* he uses fullscreen player to complete the course;
|
||||
* he rates the course;
|
||||
* they use fullscreen player to complete the course;
|
||||
* they rate the course;
|
||||
*/
|
||||
tour.register('course_member_youtube', {
|
||||
url: '/slides',
|
||||
|
||||
@@ -6,9 +6,9 @@ import slidesTourTools from '@website_slides/../tests/tours/slides_tour_tools';
|
||||
/**
|
||||
* Global use case:
|
||||
* a user (website publisher) creates a course;
|
||||
* he updates it;
|
||||
* he creates some lessons in it;
|
||||
* he publishes it;
|
||||
* they update it;
|
||||
* they create some lessons in it;
|
||||
* they publishe it;
|
||||
*/
|
||||
tour.register('course_publisher', {
|
||||
url: '/slides',
|
||||
|
||||
@@ -5,8 +5,8 @@ import tour from 'web_tour.tour';
|
||||
/**
|
||||
* Global use case:
|
||||
* - a user (website publisher) lands on the fullscreen view of a course ;
|
||||
* - he clicks on the website editor "Edit" button ;
|
||||
* - he is redirected to the non-fullscreen view with the editor opened.
|
||||
* - they click on the website editor "Edit" button ;
|
||||
* - they are redirected to the non-fullscreen view with the editor opened.
|
||||
*
|
||||
* This tour tests a fix made when editing a course in fullscreen view.
|
||||
* See "Fullscreen#_onWebEditorClick" for more information.
|
||||
|
||||
@@ -292,7 +292,7 @@ class TestAccessFeatures(common.SlidesCase):
|
||||
self.assertTrue(channel_manager.can_upload)
|
||||
self.assertTrue(channel_manager.can_publish)
|
||||
|
||||
# superuser should always be able to publish even if he's not the responsible
|
||||
# superuser should always be able to publish even if they are not the responsible
|
||||
channel_superuser = self.channel.sudo()
|
||||
channel_superuser.invalidate_cache(['can_upload', 'can_publish'])
|
||||
self.assertTrue(channel_superuser.can_upload)
|
||||
|
||||
@@ -26,10 +26,10 @@ class SurveyUserInput(models.Model):
|
||||
return res
|
||||
|
||||
def _check_for_failed_attempt(self):
|
||||
""" If the user fails his last attempt at a course certification,
|
||||
we remove him from the members of the course (and he has to enroll again).
|
||||
He receives an email in the process notifying him of his failure and suggesting
|
||||
he enrolls to the course again.
|
||||
""" If the user fails their last attempt at a course certification,
|
||||
we remove them from the members of the course (and they have to enroll again).
|
||||
They receive an email in the process notifying them of their failure and suggesting
|
||||
they enroll to the course again.
|
||||
|
||||
The purpose is to have a 'certification flow' where the user can re-purchase the
|
||||
certification when they have failed it."""
|
||||
|
||||
@@ -70,7 +70,7 @@ class TestCourseCertificationFailureFlow(TestSurveyCommon):
|
||||
self.assertFalse(slide_partner.survey_scoring_success, 'Quizz should not be marked as passed with wrong answers')
|
||||
# forces recompute of partner_ids as we delete directly in relation
|
||||
self.channel.invalidate_cache()
|
||||
self.assertIn(self.user_public.partner_id, self.channel.partner_ids, 'Public user should still be a member of the course because he still has attempts left')
|
||||
self.assertIn(self.user_public.partner_id, self.channel.partner_ids, 'Public user should still be a member of the course because they still have attempts left')
|
||||
|
||||
# Step 5: simulate a 'retry'
|
||||
retry_user_input = self.slide_certification.survey_id.sudo()._create_answer(
|
||||
@@ -85,7 +85,7 @@ class TestCourseCertificationFailureFlow(TestSurveyCommon):
|
||||
self.fill_in_answer(retry_user_input, certification.question_ids)
|
||||
# forces recompute of partner_ids as we delete directly in relation
|
||||
self.channel.invalidate_cache()
|
||||
self.assertNotIn(self.user_public.partner_id, self.channel.partner_ids, 'Public user should have been kicked out of the course because he failed his last attempt')
|
||||
self.assertNotIn(self.user_public.partner_id, self.channel.partner_ids, 'Public user should have been kicked out of the course because they failed their last attempt')
|
||||
|
||||
# Step 7: add public user as member of the channel once again
|
||||
self.channel._action_add_members(self.user_public.partner_id)
|
||||
|
||||
Reference in New Issue
Block a user