Server : Apache System : Linux iad1-shared-b8-43 6.6.49-grsec-jammy+ #10 SMP Thu Sep 12 23:23:08 UTC 2024 x86_64 User : dh_edsupp ( 6597262) PHP Version : 8.2.26 Disable Function : NONE Directory : /lib/python3/dist-packages/trac/notification/ |
Upload File : |
# -*- coding: utf-8 -*- # # Copyright (C) 2014-2021 Edgewall Software # Copyright (C) 2010 Robert Corsaro # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at https://trac.edgewall.org/wiki/TracLicense. # # This software consists of voluntary contributions made by many # individuals. For the exact contribution history, see the revision # history and logs, available at https://trac.edgewall.org/log/. from trac.util.datefmt import datetime_now, utc, to_utimestamp __all__ = ['Subscription', 'Watch'] class Subscription(object): __slots__ = ('env', 'values') fields = ('id', 'sid', 'authenticated', 'distributor', 'format', 'priority', 'adverb', 'class') def __init__(self, env): self.env = env self.values = {} def __repr__(self): values = ' '.join('%s=%r' % (name, self.values.get(name)) for name in self.fields) return '<%s %s>' % (self.__class__.__name__, values) def __getitem__(self, name): if name not in self.fields: raise KeyError(name) return self.values.get(name) def __setitem__(self, name, value): if name not in self.fields: raise KeyError(name) self.values[name] = value def _from_database(self, id, sid, authenticated, distributor, format, priority, adverb, class_): self['id'] = id self['sid'] = sid self['authenticated'] = int(authenticated) self['distributor'] = distributor self['format'] = format or None self['priority'] = int(priority) self['adverb'] = adverb self['class'] = class_ @classmethod def add(cls, env, subscription): """id and priority overwritten.""" with env.db_transaction as db: priority = len(cls.find_by_sid_and_distributor( env, subscription['sid'], subscription['authenticated'], subscription['distributor'])) + 1 now = to_utimestamp(datetime_now(utc)) cursor = db.cursor() cursor.execute(""" INSERT INTO notify_subscription (time, changetime, sid, authenticated, distributor, format, priority, adverb, class) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)""", (now, now, subscription['sid'], int(subscription['authenticated']), subscription['distributor'], subscription['format'] or None, int(priority), subscription['adverb'], subscription['class'])) return db.get_last_id(cursor, 'notify_subscription') @classmethod def delete(cls, env, rule_id, sid=None, authenticated=None): with env.db_transaction as db: kwargs = {'id': rule_id} if sid is not None or authenticated is not None: kwargs['sid'] = sid kwargs['authenticated'] = 1 if authenticated else 0 for sub in cls._find(env, **kwargs): break else: return db("DELETE FROM notify_subscription WHERE id=%s", (sub['id'],)) subs = cls.find_by_sid_and_distributor( env, sub['sid'], sub['authenticated'], sub['distributor']) now = to_utimestamp(datetime_now(utc)) values = [(new_priority, now, sub['id']) for new_priority, sub in enumerate(subs, 1) if new_priority != sub['priority']] db.executemany(""" UPDATE notify_subscription SET priority=%s, changetime=%s WHERE id=%s """, values) @classmethod def move(cls, env, rule_id, priority, sid=None, authenticated=None): with env.db_transaction as db: kwargs = {'id': rule_id} if sid is not None or authenticated is not None: kwargs['sid'] = sid kwargs['authenticated'] = 1 if authenticated else 0 for sub in cls._find(env, **kwargs): break else: return subs = cls.find_by_sid_and_distributor( env, sub['sid'], sub['authenticated'], sub['distributor']) if not (1 <= priority <= len(subs)): return for idx, sub in enumerate(subs): if sub['id'] == rule_id: break else: return subs.insert(priority - 1, subs.pop(idx)) now = to_utimestamp(datetime_now(utc)) values = [(new_priority, now, sub['id']) for new_priority, sub in enumerate(subs, 1) if new_priority != sub['priority']] db.executemany(""" UPDATE notify_subscription SET priority=%s, changetime=%s WHERE id=%s """, values) @classmethod def replace_all(cls, env, sid, authenticated, subscriptions): authenticated = int(authenticated) with env.db_transaction as db: ids_map = {} for id_, distributor, class_ in db("""\ SELECT id, distributor, class FROM notify_subscription WHERE sid=%s AND authenticated=%s""", (sid, authenticated)): ids_map.setdefault((distributor, class_), []).append(id_) for ids in ids_map.values(): ids.sort(reverse=True) priorities = {} now = to_utimestamp(datetime_now(utc)) for sub in subscriptions: distributor = sub['distributor'] priorities.setdefault(distributor, 0) priorities[distributor] += 1 prio = priorities[distributor] key = (distributor, sub['class']) if ids_map.get(key): id_ = ids_map[key].pop() db("""\ UPDATE notify_subscription SET changetime=%s,distributor=%s,format=%s,priority=%s, adverb=%s,class=%s WHERE id=%s""", (now, sub['distributor'], sub['format'] or None, prio, sub['adverb'], sub['class'], id_)) else: db("""\ INSERT INTO notify_subscription ( time,changetime,sid,authenticated,distributor, format,priority,adverb,class) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s)""", (now, now, sid, authenticated, sub['distributor'], sub['format'] or None, prio, sub['adverb'], sub['class'])) delete_ids = [] for ids in ids_map.values(): delete_ids.extend(ids) if delete_ids: db("DELETE FROM notify_subscription WHERE id IN (%s)" % ','.join(('%s',) * len(delete_ids)), delete_ids) @classmethod def update_format_by_distributor_and_sid(cls, env, distributor, sid, authenticated, format): with env.db_transaction as db: db(""" UPDATE notify_subscription SET format=%s WHERE distributor=%s AND sid=%s AND authenticated=%s """, (format or None, distributor, sid, int(authenticated))) @classmethod def _find(cls, env, order=None, **kwargs): with env.db_query as db: conditions = [] args = [] for name, value in sorted(kwargs.items()): if name.endswith('_'): name = name[:-1] if name == 'authenticated': value = int(value) conditions.append(db.quote(name) + '=%s') args.append(value) query = 'SELECT id, sid, authenticated, distributor, format, ' \ 'priority, adverb, class FROM notify_subscription' if conditions: query += ' WHERE ' + ' AND '.join(conditions) if order: if not isinstance(order, (tuple, list)): order = (order,) query += ' ORDER BY ' + \ ', '.join(db.quote(name) for name in order) cursor = db.cursor() cursor.execute(query, args) for row in cursor: sub = Subscription(env) sub._from_database(*row) yield sub @classmethod def find_by_sid_and_distributor(cls, env, sid, authenticated, distributor): return list(cls._find(env, sid=sid, authenticated=authenticated, distributor=distributor, order='priority')) @classmethod def find_by_sids_and_class(cls, env, uids, class_): """uids should be a collection to tuples (sid, auth)""" subs = [] for sid, authenticated in uids: subs.extend(cls._find(env, class_=class_, sid=sid, authenticated=authenticated, order='priority')) return subs @classmethod def find_by_class(cls, env, class_): return list(cls._find(env, class_=class_)) def subscription_tuple(self): return ( self.values['class'], self.values['distributor'], self.values['sid'], self.values['authenticated'], None, self.values['format'] or None, int(self.values['priority']), self.values['adverb'] ) def _update_priority(self): with self.env.db_transaction as db: cursor = db.cursor() now = to_utimestamp(datetime_now(utc)) cursor.execute(""" UPDATE notify_subscription SET changetime=%s, priority=%s WHERE id=%s """, (now, int(self.values['priority']), self.values['id'])) class Watch(object): __slots__ = ('env', 'values') fields = ('id', 'sid', 'authenticated', 'class', 'realm', 'target') def __init__(self, env): self.env = env self.values = {} def __getitem__(self, name): if name not in self.fields: raise KeyError(name) return self.values.get(name) def __setitem__(self, name, value): if name not in self.fields: raise KeyError(name) self.values[name] = value def _from_database(self, id, sid, authenticated, class_, realm, target): self['id'] = id self['sid'] = sid self['authenticated'] = int(authenticated) self['class'] = class_ self['realm'] = realm self['target'] = target @classmethod def add(cls, env, sid, authenticated, class_, realm, targets): with env.db_transaction as db: for target in targets: db(""" INSERT INTO notify_watch (sid, authenticated, class, realm, target) VALUES (%s, %s, %s, %s, %s) """, (sid, int(authenticated), class_, realm, target)) @classmethod def delete(cls, env, watch_id): with env.db_transaction as db: db("DELETE FROM notify_watch WHERE id = %s", (watch_id,)) @classmethod def delete_by_sid_and_class(cls, env, sid, authenticated, class_): with env.db_transaction as db: db(""" DELETE FROM notify_watch WHERE sid = %s AND authenticated = %s AND class = %s """, (sid, int(authenticated), class_)) @classmethod def delete_by_class_realm_and_target(cls, env, class_, realm, target): with env.db_transaction as db: db(""" DELETE FROM notify_watch WHERE class = %s AND realm = %s AND target = %s """, (realm, class_, target)) @classmethod def _find(cls, env, order=None, **kwargs): with env.db_query as db: conditions = [] args = [] for name, value in sorted(kwargs.items()): if name.endswith('_'): name = name[:-1] if name == 'authenticated': value = int(value) conditions.append(db.quote(name) + '=%s') if name == 'authenticated': value = int(value) args.append(value) query = 'SELECT id, sid, authenticated, class, realm, target ' \ 'FROM notify_watch' if conditions: query += ' WHERE ' + ' AND '.join(conditions) if order: if not isinstance(order, (tuple, list)): order = (order,) query += ' ORDER BY ' + \ ', '.join(db.quote(name) for name in order) cursor = db.cursor() cursor.execute(query, args) for row in cursor: watch = Watch(env) watch._from_database(*row) yield watch @classmethod def find_by_sid_and_class(cls, env, sid, authenticated, class_): return list(cls._find(env, sid=sid, authenticated=authenticated, class_=class_, order='target')) @classmethod def find_by_sid_class_realm_and_target(cls, env, sid, authenticated, class_, realm, target): return list(cls._find(env, sid=sid, authenticated=authenticated, class_=class_, realm=realm, order='target')) @classmethod def find_by_class_realm_and_target(cls, env, class_, realm, target): return list(cls._find(env, class_=class_, realm=realm, target=target)) @classmethod def find_by_class_and_realm(cls, env, class_, realm): return list(cls._find(env, class_=class_, realm=realm))