def _execute_command_lead(self, **kwargs): partner = self.env.user.partner_id key = kwargs['body'] channel_partners = self.env['mail.channel.partner'].search( [('partner_id', '!=', partner.id), ('channel_id', '=', self.id)], limit=1) if key.strip() == '/lead': msg = self._define_command_lead()['help'] else: description = ''.join( '%s: %s\n' % (message.author_id.name or self.anonymous_name, message.body) for message in self.channel_message_ids.sorted('id')) lead = self.env['crm.lead'].create({ 'name': html2plaintext(key[5:]), 'partner_id': channel_partners.partner_id.id, 'user_id': None, 'team_id': None, 'description': html2plaintext(description), 'referred': partner.name }) lead._onchange_partner_id() msg = _( 'Created a new lead: <a href="#" data-oe-id="%s" data-oe-model="crm.lead">%s</a>' ) % (lead.id, lead.name) self._send_transient_message(partner, msg)
def _compute_teaser(self): for blog_post in self: if blog_post.teaser_manual: blog_post.teaser = blog_post.teaser_manual else: content = html2plaintext(blog_post.content).replace('\n', ' ') blog_post.teaser = content[:150] + '...'
def _compute_description(self): for message in self: if message.subject: message.description = message.subject else: plaintext_ct = '' if not message.body else html2plaintext(message.body) message.description = plaintext_ct[:30] + '%s' % (' [...]' if len(plaintext_ct) >= 30 else '')
def pad_generate_url(self): company = self.env.user.sudo().company_id pad = { "server": company.pad_server, "key": company.pad_key, } # make sure pad server in the form of http://hostname if not pad["server"]: return pad if not pad["server"].startswith('http'): pad["server"] = 'http://' + pad["server"] pad["server"] = pad["server"].rstrip('/') # generate a salt s = string.ascii_uppercase + string.digits salt = ''.join([ s[random.SystemRandom().randint(0, len(s) - 1)] for i in range(10) ]) # path # etherpad hardcodes pad id length limit to 50 path = '-%s-%s' % (self._name, salt) path = '%s%s' % (self.env.cr.dbname.replace( '_', '-')[0:50 - len(path)], path) # contruct the url url = '%s/p/%s' % (pad["server"], path) # if create with content if self.env.context.get('field_name') and self.env.context.get( 'model') and self.env.context.get('object_id'): myPad = EtherpadLiteClient(pad["key"], pad["server"] + '/api') try: myPad.createPad(path) except IOError: raise UserError( _("Pad creation failed, either there is a problem with your pad server URL or with your connection." )) # get attr on the field model model = self.env[self.env.context["model"]] field = model._fields[self.env.context['field_name']] real_field = field.pad_content_field # get content of the real field for record in model.browse([self.env.context["object_id"]]): if record[real_field]: myPad.setText( path, (html2plaintext(record[real_field]).encode('utf-8'))) # Etherpad for html not functional # myPad.setHTML(path, record[real_field]) return { "server": pad["server"], "path": path, "url": url, }
def setHtmlFallbackText(self, padID, html): try: # Prevents malformed HTML errors html_wellformed = '<html><body>' + html + '</body></html>' return self.setHtml(padID, html_wellformed) except Exception: _logger.exception( 'Falling back to setText. SetHtml failed with message:') return self.setText(padID, html2plaintext(html).encode('UTF-8'))
def _compute_teaser(self): for blog_post in self: if blog_post.teaser_manual: blog_post.teaser = blog_post.teaser_manual else: content = html2plaintext(blog_post.content).replace('\n', ' ') blog_post.teaser = ' '.join( itertools.islice( (c for c in content.split(' ') if c), 50)) + '...'
def assertSMSNotification(self, recipients_info, content, messages=None, check_sms=True, sent_unlink=False): """ Check content of notifications. :param recipients_info: list[{ 'partner': res.partner record (may be empty), 'number': number used for notification (may be empty, computed based on partner), 'state': ready / sent / exception / canceled (sent by default), 'failure_type': optional: sms_number_missing / sms_number_format / sms_credit / sms_server }, { ... }] """ partners = self.env['res.partner'].concat(*list(p['partner'] for p in recipients_info if p.get('partner'))) numbers = [p['number'] for p in recipients_info if p.get('number')] # special case of void notifications: check for False / False notifications if not partners and not numbers: numbers = [False] base_domain = [ '|', ('res_partner_id', 'in', partners.ids), '&', ('res_partner_id', '=', False), ('sms_number', 'in', numbers), ('notification_type', '=', 'sms') ] if messages is not None: base_domain += [('mail_message_id', 'in', messages.ids)] notifications = self.env['mail.notification'].search(base_domain) self.assertEqual(notifications.mapped('res_partner_id'), partners) for recipient_info in recipients_info: partner = recipient_info.get('partner', self.env['res.partner']) number = recipient_info.get('number') state = recipient_info.get('state', 'sent') if number is None and partner: number = partner.phone_get_sanitized_number() notif = notifications.filtered(lambda n: n.res_partner_id == partner and n.sms_number == number and n.notification_status == state) self.assertTrue(notif, 'SMS: not found notification for %s (number: %s, state: %s)' % (partner, number, state)) if state not in ('sent', 'ready', 'canceled'): self.assertEqual(notif.failure_type, recipient_info['failure_type']) if check_sms: if state == 'sent': if sent_unlink: self.assertSMSIapSent([number], content=content) else: self.assertSMS(partner, number, 'sent', content=content) elif state == 'ready': self.assertSMS(partner, number, 'outgoing', content=content) elif state == 'exception': self.assertSMS(partner, number, 'error', error_code=recipient_info['failure_type'], content=content) elif state == 'canceled': self.assertSMS(partner, number, 'canceled', error_code=recipient_info['failure_type'], content=content) else: raise NotImplementedError('Not implemented') if messages is not None: for message in messages: self.assertEqual(content, tools.html2plaintext(message.body).rstrip('\n'))
def _convert_visitor_to_lead(self, partner, channel_partners, key): """ Create a lead from channel /lead command :param partner: internal user partner (operator) that created the lead; :param channel_partners: channel members; :param key: operator input in chat ('/lead Lead about Product') """ description = ''.join( '%s: %s\n' % (message.author_id.name or self.anonymous_name, message.body) for message in self.channel_message_ids.sorted('id')) # if public user is part of the chat: consider lead to be linked to an # anonymous user whatever the participants. Otherwise keep only share # partners (no user or portal user) to link to the lead. customers = self.env['res.partner'] for customer in channel_partners.partner_id.filtered( 'partner_share').with_context(active_test=False): if customer.user_ids and all(user._is_public() for user in customer.user_ids): customers = self.env['res.partner'] break else: customers |= customer utm_source = self.env.ref('crm_livechat.utm_source_livechat', raise_if_not_found=False) return self.env['crm.lead'].create({ 'name': html2plaintext(key[5:]), 'partner_id': customers[0].id if customers else False, 'user_id': False, 'team_id': False, 'description': html2plaintext(description), 'referred': partner.name, 'source_id': utm_source and utm_source.id, })
def _message_sms(self, body, subtype_id=False, partner_ids=False, number_field=False, sms_numbers=None, sms_pid_to_number=None, **kwargs): """ Main method to post a message on a record using SMS-based notification method. :param body: content of SMS; :param subtype_id: mail.message.subtype used in mail.message associated to the sms notification process; :param partner_ids: if set is a record set of partners to notify; :param number_field: if set is a name of field to use on current record to compute a number to notify; :param sms_numbers: see ``_notify_record_by_sms``; :param sms_pid_to_number: see ``_notify_record_by_sms``; """ self.ensure_one() sms_pid_to_number = sms_pid_to_number if sms_pid_to_number is not None else {} if number_field or (partner_ids is False and sms_numbers is None): info = self._sms_get_recipients_info( force_field=number_field)[self.id] info_partner_ids = info['partner'].ids if info['partner'] else False info_number = info['sanitized'] if info['sanitized'] else info[ 'number'] if info_partner_ids and info_number: sms_pid_to_number[info_partner_ids[0]] = info_number if info_partner_ids: partner_ids = info_partner_ids + (partner_ids or []) if not info_partner_ids: if info_number: sms_numbers = [info_number] + (sms_numbers or []) # will send a falsy notification allowing to fix it through SMS wizards elif not sms_numbers: sms_numbers = [False] if subtype_id is False: subtype_id = self.env['ir.model.data'].xmlid_to_res_id( 'mail.mt_note') return self.message_post( body=plaintext2html(html2plaintext(body)), partner_ids=partner_ids or [], # TDE FIXME: temp fix otherwise crash mail_thread.py message_type='sms', subtype_id=subtype_id, sms_numbers=sms_numbers, sms_pid_to_number=sms_pid_to_number, **kwargs)
def message_new(self, msg, custom_values=None): if custom_values is None: custom_values = {} desc = html2plaintext(msg.get('body')) if msg.get('body') else '' defaults = { 'name': msg.get('subject') or _("No Subject"), 'description': desc, 'email_from': msg.get('from'), 'email_cc': msg.get('cc'), 'partner_id': msg.get('author_id', False), } if msg.get('priority'): defaults['priority'] = msg.get('priority') defaults.update(custom_values) return super(crm_claim, self).message_new(msg, custom_values=defaults)
def send_get_email_dict(self, partner=None): """Return a dictionary for specific email values, depending on a partner, or generic to the whole recipients given by mail.email_to. :param Model partner: specific recipient partner """ self.ensure_one() body = self.send_get_mail_body(partner=partner) body_alternative = tools.html2plaintext(body) res = { 'body': body, 'body_alternative': body_alternative, 'email_to': self.send_get_mail_to(partner=partner), } return res
def _get_discussion_detail(self, ids, publish=False, **post): values = [] for message in request.env['mail.message'].sudo().browse(ids): values.append({ "id": message.id, "author_name": message.author_id.name, "author_image": message.author_id.image and \ (b"data:image/png;base64,%s" % message.author_id.image) or \ b'/website_blog/static/src/img/anonymous.png', "date": message.date, 'body': html2plaintext(message.body), 'website_published' : message.website_published, 'publish' : publish, }) return values
def _set_pad_value(self, vals): # Update the pad if the `pad_content_field` is modified for k, field in self._fields.items(): if hasattr(field, 'pad_content_field') and vals.get(field.pad_content_field) and self[k]: company = self.env.user.sudo().company_id myPad = EtherpadLiteClient(company.pad_key, company.pad_server + '/api') path = self[k].split('/p/')[1] myPad.setText(path, (html2plaintext(vals[field.pad_content_field]).encode('utf-8'))) # Update the `pad_content_field` if the pad is modified for k, v in list(vals.items()): field = self._fields[k] if hasattr(field, 'pad_content_field'): vals[field.pad_content_field] = self.pad_get_content(v)
def action_create_calendar_event(self): self.ensure_one() action = self.env.ref('calendar.action_calendar_event').read()[0] action['context'] = { 'default_activity_type_id': self.activity_type_id.id, 'default_res_id': self.env.context.get('default_res_id'), 'default_res_model': self.env.context.get('default_res_model'), 'default_name': self.summary, 'default_description': self.note and tools.html2plaintext(self.note) or '', 'default_activity_ids': [(6, 0, self.ids)], } return action
def action_create_calendar_event(self): self.ensure_one() action = self.env["ir.actions.actions"]._for_xml_id( "calendar.action_calendar_event") action['context'] = { 'default_activity_type_id': self.activity_type_id.id, 'default_res_id': self.env.context.get('default_res_id'), 'default_res_model': self.env.context.get('default_res_model'), 'default_name': self.summary or self.res_name, 'default_description': self.note and tools.html2plaintext(self.note).strip() or '', 'default_activity_ids': [(6, 0, self.ids)], } return action
def _send_prepare_values(self, partner=None): """Return a dictionary for specific email values, depending on a partner, or generic to the whole recipients given by mail.email_to. :param Model partner: specific recipient partner """ self.ensure_one() body = self._send_prepare_body() body_alternative = tools.html2plaintext(body) if partner: email_to = [ tools.formataddr((partner.name or 'False', partner.email or 'False')) ] else: email_to = tools.email_split_and_format(self.email_to) res = { 'body': body, 'body_alternative': body_alternative, 'email_to': email_to, } return res
def _mark_done(self): """ Will add certification to employee's resumé if - The survey is a certification - The user is linked to an employee - The user succeeded the test """ super(SurveyUserInput, self)._mark_done() certification_user_inputs = self.filtered(lambda user_input: user_input.survey_id.certification and user_input.scoring_success) partner_has_completed = {user_input.partner_id.id: user_input.survey_id for user_input in certification_user_inputs} employees = self.env['hr.employee'].sudo().search([('user_id.partner_id', 'in', certification_user_inputs.mapped('partner_id').ids)]) for employee in employees: line_type = self.env.ref('hr_skills_survey.resume_type_certification', raise_if_not_found=False) survey = partner_has_completed.get(employee.user_id.partner_id.id) self.env['hr.resume.line'].create({ 'employee_id': employee.id, 'name': survey.title, 'date_start': fields.Date.today(), 'date_end': fields.Date.today(), 'description': html2plaintext(survey.description), 'line_type_id': line_type and line_type.id, 'display_type': 'certification', 'survey_id': survey.id })
def _notify_record_by_sms(self, message, recipients_data, msg_vals=False, sms_numbers=None, sms_pid_to_number=None, check_existing=False, put_in_queue=False, **kwargs): """ Notification method: by SMS. :param message: mail.message record to notify; :param recipients_data: see ``_notify_thread``; :param msg_vals: see ``_notify_thread``; :param sms_numbers: additional numbers to notify in addition to partners and classic recipients; :param pid_to_number: force a number to notify for a given partner ID instead of taking its mobile / phone number; :param check_existing: check for existing notifications to update based on mailed recipient, otherwise create new notifications; :param put_in_queue: use cron to send queued SMS instead of sending them directly; """ sms_pid_to_number = sms_pid_to_number if sms_pid_to_number is not None else {} sms_numbers = sms_numbers if sms_numbers is not None else [] sms_create_vals = [] sms_all = self.env['sms.sms'].sudo() # pre-compute SMS data body = msg_vals['body'] if msg_vals and msg_vals.get( 'body') else message.body sms_base_vals = { 'body': html2plaintext(body), 'mail_message_id': message.id, 'state': 'outgoing', } # notify from computed recipients_data (followers, specific recipients) partners_data = [ r for r in recipients_data['partners'] if r['notif'] == 'sms' ] partner_ids = [r['id'] for r in partners_data] if partner_ids: for partner in self.env['res.partner'].sudo().browse(partner_ids): number = sms_pid_to_number.get( partner.id) or partner.mobile or partner.phone sanitize_res = phone_validation.phone_sanitize_numbers_w_record( [number], partner)[number] number = sanitize_res['sanitized'] or number sms_create_vals.append( dict(sms_base_vals, partner_id=partner.id, number=number)) # notify from additional numbers if sms_numbers: sanitized = phone_validation.phone_sanitize_numbers_w_record( sms_numbers, self) tocreate_numbers = [ value['sanitized'] or original for original, value in sanitized.items() ] sms_create_vals += [ dict( sms_base_vals, partner_id=False, number=n, state='outgoing' if n else 'error', error_code='' if n else 'sms_number_missing', ) for n in tocreate_numbers ] # create sms and notification existing_pids, existing_numbers = [], [] if sms_create_vals: sms_all |= self.env['sms.sms'].sudo().create(sms_create_vals) if check_existing: existing = self.env['mail.notification'].sudo().search([ '|', ('res_partner_id', 'in', partner_ids), '&', ('res_partner_id', '=', False), ('sms_number', 'in', sms_numbers), ('notification_type', '=', 'sms'), ('mail_message_id', '=', message.id) ]) for n in existing: if n.res_partner_id.id in partner_ids and n.mail_message_id == message: existing_pids.append(n.res_partner_id.id) if not n.res_partner_id and n.sms_number in sms_numbers and n.mail_message_id == message: existing_numbers.append(n.sms_number) notif_create_values = [ { 'mail_message_id': message.id, 'res_partner_id': sms.partner_id.id, 'sms_number': sms.number, 'notification_type': 'sms', 'sms_id': sms.id, 'is_read': True, # discard Inbox notification 'notification_status': 'ready' if sms.state == 'outgoing' else 'exception', 'failure_type': '' if sms.state == 'outgoing' else sms.error_code, } for sms in sms_all if (sms.partner_id and sms.partner_id.id not in existing_pids) or (not sms.partner_id and sms.number not in existing_numbers) ] if notif_create_values: self.env['mail.notification'].sudo().create( notif_create_values) if existing_pids or existing_numbers: for sms in sms_all: notif = next( (n for n in existing if (n.res_partner_id.id in existing_pids and n.res_partner_id.id == sms.partner_id.id) or (not n.res_partner_id and n.sms_number in existing_numbers and n.sms_number == sms.number)), False) if notif: notif.write({ 'notification_type': 'sms', 'notification_status': 'ready', 'sms_id': sms.id, 'sms_number': sms.number, }) if sms_all and not put_in_queue: sms_all.filtered(lambda sms: sms.state == 'outgoing').send( auto_commit=False, raise_exception=False) return True
def name_get(self): return [(ribbon.id, '%s (#%d)' % (tools.html2plaintext(ribbon.html), ribbon.id)) for ribbon in self]
def _get_plain_content(self): self.plain_content = tools.html2plaintext(self.content)[0:500] if self.content else False
def assertSMSLogged(self, records, body): for record in records: message = record.message_ids[-1] self.assertEqual(message.subtype_id, self.env.ref('mail.mt_note')) self.assertEqual(message.message_type, 'sms') self.assertEqual(tools.html2plaintext(message.body).rstrip('\n'), body)
def _compute_name(self): """ Read the first line of the memo to determine the note name """ for note in self: text = html2plaintext(note.memo) if note.memo else '' note.name = text.strip().replace('*', '').split("\n")[0]
def build_email(self, email_from, email_to, subject, body, email_cc=None, email_bcc=None, reply_to=False, attachments=None, message_id=None, references=None, object_id=False, subtype='plain', headers=None, body_alternative=None, subtype_alternative='plain'): """Constructs an RFC2822 email.message.Message object based on the keyword arguments passed, and returns it. :param string email_from: sender email address :param list email_to: list of recipient addresses (to be joined with commas) :param string subject: email subject (no pre-encoding/quoting necessary) :param string body: email body, of the type ``subtype`` (by default, plaintext). If html subtype is used, the message will be automatically converted to plaintext and wrapped in multipart/alternative, unless an explicit ``body_alternative`` version is passed. :param string body_alternative: optional alternative body, of the type specified in ``subtype_alternative`` :param string reply_to: optional value of Reply-To header :param string object_id: optional tracking identifier, to be included in the message-id for recognizing replies. Suggested format for object-id is "res_id-model", e.g. "12345-crm.lead". :param string subtype: optional mime subtype for the text body (usually 'plain' or 'html'), must match the format of the ``body`` parameter. Default is 'plain', making the content part of the mail "text/plain". :param string subtype_alternative: optional mime subtype of ``body_alternative`` (usually 'plain' or 'html'). Default is 'plain'. :param list attachments: list of (filename, filecontents) pairs, where filecontents is a string containing the bytes of the attachment :param list email_cc: optional list of string values for CC header (to be joined with commas) :param list email_bcc: optional list of string values for BCC header (to be joined with commas) :param dict headers: optional map of headers to set on the outgoing mail (may override the other headers, including Subject, Reply-To, Message-Id, etc.) :rtype: email.message.EmailMessage :return: the new RFC2822 email message """ email_from = email_from or self._get_default_from_address() assert email_from, "You must either provide a sender address explicitly or configure "\ "using the combination of `mail.catchall.domain` and `mail.default.from` "\ "ICPs, in the server configuration file or with the "\ "--email-from startup parameter." headers = headers or {} # need valid dict later email_cc = email_cc or [] email_bcc = email_bcc or [] body = body or u'' msg = EmailMessage(policy=email.policy.SMTP) msg.set_charset('utf-8') if not message_id: if object_id: message_id = tools.generate_tracking_message_id(object_id) else: message_id = make_msgid() msg['Message-Id'] = message_id if references: msg['references'] = references msg['Subject'] = subject email_from, return_path = self._get_email_from(email_from) msg['From'] = email_from if return_path: headers.setdefault('Return-Path', return_path) del msg['Reply-To'] msg['Reply-To'] = reply_to or email_from msg['To'] = email_to if email_cc: msg['Cc'] = email_cc if email_bcc: msg['Bcc'] = email_bcc msg['Date'] = datetime.datetime.utcnow() for key, value in headers.items(): msg[pycompat.to_text(ustr(key))] = value email_body = ustr(body) if subtype == 'html' and not body_alternative: msg.add_alternative(tools.html2plaintext(email_body), subtype='plain', charset='utf-8') msg.add_alternative(email_body, subtype=subtype, charset='utf-8') elif body_alternative: msg.add_alternative(ustr(body_alternative), subtype=subtype_alternative, charset='utf-8') msg.add_alternative(email_body, subtype=subtype, charset='utf-8') else: msg.set_content(email_body, subtype=subtype, charset='utf-8') if attachments: for (fname, fcontent, mime) in attachments: maintype, subtype = mime.split('/') if mime and '/' in mime else ('application', 'octet-stream') msg.add_attachment(fcontent, maintype, subtype, filename=fname) return msg
def _microsoft_values(self, fields_to_sync, initial_values={}): values = dict(initial_values) if not fields_to_sync: return values values['id'] = self.microsoft_id microsoft_guid = self.env['ir.config_parameter'].sudo().get_param('microsoft_calendar.microsoft_guid', False) values['singleValueExtendedProperties'] = [{ 'id': 'String {%s} Name flectra_id' % microsoft_guid, 'value': str(self.id), }, { 'id': 'String {%s} Name owner_flectra_id' % microsoft_guid, 'value': str(self.user_id.id), }] if self.microsoft_recurrence_master_id and 'type' not in values: values['seriesMasterId'] = self.microsoft_recurrence_master_id values['type'] = 'exception' if 'name' in fields_to_sync: values['subject'] = self.name or '' if 'description' in fields_to_sync: values['body'] = { 'content': html2plaintext(self.description) if not is_html_empty(self.description) else '', 'contentType': "text", } if any(x in fields_to_sync for x in ['allday', 'start', 'date_end', 'stop']): if self.allday: start = {'dateTime': self.start_date.isoformat(), 'timeZone': 'Europe/London'} end = {'dateTime': (self.stop_date + relativedelta(days=1)).isoformat(), 'timeZone': 'Europe/London'} else: start = {'dateTime': pytz.utc.localize(self.start).isoformat(), 'timeZone': 'Europe/London'} end = {'dateTime': pytz.utc.localize(self.stop).isoformat(), 'timeZone': 'Europe/London'} values['start'] = start values['end'] = end values['isAllDay'] = self.allday if 'location' in fields_to_sync: values['location'] = {'displayName': self.location or ''} if 'alarm_ids' in fields_to_sync: alarm_id = self.alarm_ids.filtered(lambda a: a.alarm_type == 'notification')[:1] values['isReminderOn'] = bool(alarm_id) values['reminderMinutesBeforeStart'] = alarm_id.duration_minutes if 'user_id' in fields_to_sync: values['organizer'] = {'emailAddress': {'address': self.user_id.email or '', 'name': self.user_id.display_name or ''}} values['isOrganizer'] = self.user_id == self.env.user if 'attendee_ids' in fields_to_sync: attendees = self.attendee_ids.filtered(lambda att: att.partner_id not in self.user_id.partner_id) values['attendees'] = [ { 'emailAddress': {'address': attendee.email or '', 'name': attendee.display_name or ''}, 'status': {'response': self._get_attendee_status_o2m(attendee)} } for attendee in attendees] if 'privacy' in fields_to_sync or 'show_as' in fields_to_sync: values['showAs'] = self.show_as sensitivity_o2m = { 'public': 'normal', 'private': 'private', 'confidential': 'confidential', } values['sensitivity'] = sensitivity_o2m.get(self.privacy) if 'active' in fields_to_sync and not self.active: values['isCancelled'] = True if values.get('type') == 'seriesMaster': recurrence = self.recurrence_id pattern = { 'interval': recurrence.interval } if recurrence.rrule_type in ['daily', 'weekly']: pattern['type'] = recurrence.rrule_type else: prefix = 'absolute' if recurrence.month_by == 'date' else 'relative' pattern['type'] = recurrence.rrule_type and prefix + recurrence.rrule_type.capitalize() if recurrence.month_by == 'date': pattern['dayOfMonth'] = recurrence.day if recurrence.month_by == 'day' or recurrence.rrule_type == 'weekly': pattern['daysOfWeek'] = [ weekday_name for weekday_name, weekday in { 'monday': recurrence.mo, 'tuesday': recurrence.tu, 'wednesday': recurrence.we, 'thursday': recurrence.th, 'friday': recurrence.fr, 'saturday': recurrence.sa, 'sunday': recurrence.su, }.items() if weekday] pattern['firstDayOfWeek'] = 'sunday' if recurrence.rrule_type == 'monthly' and recurrence.month_by == 'day': byday_selection = { '1': 'first', '2': 'second', '3': 'third', '4': 'fourth', '-1': 'last', } pattern['index'] = byday_selection[recurrence.byday] dtstart = recurrence.dtstart or fields.Datetime.now() rule_range = { 'startDate': (dtstart.date()).isoformat() } if recurrence.end_type == 'count': # e.g. stop after X occurence rule_range['numberOfOccurrences'] = min(recurrence.count, MAX_RECURRENT_EVENT) rule_range['type'] = 'numbered' elif recurrence.end_type == 'forever': rule_range['numberOfOccurrences'] = MAX_RECURRENT_EVENT rule_range['type'] = 'numbered' elif recurrence.end_type == 'end_date': # e.g. stop after 12/10/2020 rule_range['endDate'] = recurrence.until.isoformat() rule_range['type'] = 'endDate' values['recurrence'] = { 'pattern': pattern, 'range': rule_range } return values
def _get_note_first_line(self): for message in self: message.name = (message.message and html2plaintext(message.message) or "").strip().replace('*', '').split("\n")[0]
def mail_update_message(self, res_model, res_id, message, message_id, redirect=None, attachment_ids='', attachment_tokens='', **post): # keep this mechanism intern to slide currently (saas 12.5) as it is # considered experimental if res_model != 'slide.channel': raise Forbidden() res_id = int(res_id) attachment_ids = [ int(attachment_id) for attachment_id in attachment_ids.split(',') if attachment_id ] attachment_tokens = [ attachment_token for attachment_token in attachment_tokens.split(',') if attachment_token ] self._portal_post_check_attachments(attachment_ids, attachment_tokens) pid = int(post['pid']) if post.get('pid') else False if not _check_special_access(res_model, res_id, token=post.get('token'), _hash=post.get('hash'), pid=pid): raise Forbidden() # fetch and update mail.message message_id = int(message_id) message_body = plaintext2html(message) domain = [('model', '=', res_model), ('res_id', '=', res_id), ('is_internal', '=', False), ('author_id', '=', request.env.user.partner_id.id), ('message_type', '=', 'comment'), ('id', '=', message_id)] # restrict to the given message_id message = request.env['mail.message'].search(domain, limit=1) if not message: raise NotFound() message.sudo().write({ 'body': message_body, 'attachment_ids': [(4, aid) for aid in attachment_ids], }) # update rating if post.get('rating_value'): domain = [('res_model', '=', res_model), ('res_id', '=', res_id), ('is_internal', '=', False), ('message_id', '=', message.id)] rating = request.env['rating.rating'].sudo().search( domain, order='write_date DESC', limit=1) rating.write({ 'rating': float(post['rating_value']), 'feedback': html2plaintext(message.body), }) # redirect to specified or referrer or simply channel page as fallback redirect_url = redirect or (request.httprequest.referrer and request.httprequest.referrer + '#review') or '/slides/%s' % res_id return werkzeug.utils.redirect(redirect_url, 302)