# This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. from textwrap import TextWrapper from sql import Null from sql.conditionals import Case from trytond.i18n import lazy_gettext from trytond.model import ModelSQL, ModelStorage, ModelView, fields from trytond.pool import Pool from trytond.pyson import Eval from trytond.tools import grouped_slice, reduce_ids from trytond.transaction import Transaction from .resource import ResourceMixin, resource_copy __all__ = ['NoteCopyMixin'] class Note(ResourceMixin, ModelSQL, ModelView): "Note" __name__ = 'ir.note' message = fields.Text('Message', states={ 'readonly': Eval('id', 0) > 0, }) message_wrapped = fields.Function(fields.Text('Message'), 'on_change_with_message_wrapped') unread = fields.Function(fields.Boolean('Unread'), 'get_unread', searcher='search_unread', setter='set_unread') @staticmethod def default_unread(): return False @classmethod def get_wrapper(cls): return TextWrapper(width=79) @fields.depends('message') def on_change_with_message_wrapped(self, name=None): wrapper = self.get_wrapper() message = self.message or '' return '\n'.join(map(wrapper.fill, message.splitlines())) @classmethod def get_unread(cls, ids, name): pool = Pool() Read = pool.get('ir.note.read') cursor = Transaction().connection.cursor() user_id = Transaction().user table = cls.__table__() read = Read.__table__() unread = {} for sub_ids in grouped_slice(ids): where = reduce_ids(table.id, sub_ids) query = table.join(read, 'LEFT', condition=(table.id == read.note) & (read.user == user_id) ).select(table.id, Case((read.user != Null, False), else_=True), where=where) cursor.execute(*query) unread.update(cursor) return unread @classmethod def search_unread(cls, name, clause): pool = Pool() Read = pool.get('ir.note.read') user_id = Transaction().user table = cls.__table__() read = Read.__table__() _, operator, value = clause assert operator in ['=', '!='] Operator = fields.SQL_OPERATORS[operator] where = Operator(Case((read.user != Null, False), else_=True), value) query = table.join(read, 'LEFT', condition=(table.id == read.note) & (read.user == user_id) ).select(table.id, where=where) return [('id', 'in', query)] @classmethod def set_unread(cls, notes, name, value): pool = Pool() Read = pool.get('ir.note.read') user_id = Transaction().user if not value: Read.create([{'note': n.id, 'user': user_id} for n in notes]) else: reads = [] for sub_notes in grouped_slice(notes): reads += Read.search([ ('note', 'in', [n.id for n in sub_notes]), ('user', '=', user_id), ]) Read.delete(reads) @classmethod def write(cls, notes, values, *args): # Avoid changing write meta data if only unread is set if args or set(values.keys()) != {'unread'}: super(Note, cls).write(notes, values, *args) else: # Check access write and clean cache # Use __func__ to directly access ModelStorage's write method and # pass it the right class ModelStorage.write.__func__(cls, notes, values) cls.set_unread(notes, 'unread', values['unread']) class NoteRead(ModelSQL): "Note Read" __name__ = 'ir.note.read' note = fields.Many2One('ir.note', 'Note', required=True, ondelete='CASCADE') user = fields.Many2One('res.user', 'User', required=True, ondelete='CASCADE') class NoteCopyMixin( resource_copy('ir.note', 'notes', lazy_gettext('ir.msg_notes'))): pass