def getRecentChanges(self, req, since): """ Get list of changed pages since timestamp """ since = to_utimestamp(since) wiki_realm = Resource('wiki') query = ('SELECT name, time, author, version, comment ' 'FROM wiki w1 ' 'WHERE time >= %s ' 'AND version = (SELECT MAX(version) ' ' FROM wiki w2 ' ' WHERE w2.name=w1.name) ' 'ORDER BY time DESC') if hasattr(self.env, 'db_query'): generator = self.env.db_query(query, (since,)) else: db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute(query, (since,)) generator = cursor result = [] for name, when, author, version, comment in generator: if 'WIKI_VIEW' in req.perm(wiki_realm(id=name, version=version)): result.append( self._page_info(name, from_utimestamp(when), author, version, comment)) return result
def test_update_time_changed(self): # Update with collision check import datetime from tracrpc.util import to_utimestamp from tracrpc.xml_rpc import from_xmlrpc_datetime tid = self.admin.ticket.create('test_update_time_changed', '...', {}) tid, created, modified, attrs = self.admin.ticket.get(tid) then = from_xmlrpc_datetime(modified) - datetime.timedelta(minutes=1) # Unrestricted old-style update (to be removed soon) try: self.admin.ticket.update(tid, "comment1", {'_ts': str(to_utimestamp(then))}) except Exception, e: self.assertTrue("Ticket has been updated since last get" in str(e))
def getRecentChanges(self, req, since): """Returns a list of IDs of tickets that have changed since timestamp.""" since = to_utimestamp(since) db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute('SELECT id FROM ticket' ' WHERE changetime >= %s', (since, )) result = [] ticket_realm = Resource('ticket') for row in cursor: tid = int(row[0]) if 'TICKET_VIEW' in req.perm(ticket_realm(id=tid)): result.append(tid) return result
def getRecentChanges(self, req, since): """Returns a list of IDs of tickets that have changed since timestamp.""" since = to_utimestamp(since) query = 'SELECT id FROM ticket WHERE changetime >= %s' if hasattr(self.env, 'db_query'): generator = self.env.db_query(query, (since, )) else: db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute(query, (since, )) generator = cursor result = [] ticket_realm = Resource('ticket') for row in generator: tid = int(row[0]) if 'TICKET_VIEW' in req.perm(ticket_realm(id=tid)): result.append(tid) return result
def getRecentChanges(self, req, since): """Returns a list of IDs of tickets that have changed since timestamp.""" since = to_utimestamp(since) query = 'SELECT id FROM ticket WHERE changetime >= %s' if hasattr(self.env, 'db_query'): generator = self.env.db_query(query, (since,)) else: db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute(query, (since,)) generator = cursor result = [] ticket_realm = Resource('ticket') for row in generator: tid = int(row[0]) if 'TICKET_VIEW' in req.perm(ticket_realm(id=tid)): result.append(tid) return result
def getRecentChanges(self, req, since): """ Get list of changed pages since timestamp """ since = to_utimestamp(since) wiki_realm = Resource("wiki") db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute( "SELECT name, time, author, version, comment " "FROM wiki w1 " "WHERE time >= %s " "AND version = (SELECT MAX(version) " " FROM wiki w2 " " WHERE w2.name=w1.name) " "ORDER BY time DESC", (since,), ) result = [] for name, when, author, version, comment in cursor: if "WIKI_VIEW" in req.perm(wiki_realm(id=name, version=version)): result.append(self._page_info(name, from_utimestamp(when), author, version, comment)) return result
import datetime from tracrpc.util import to_utimestamp from tracrpc.xml_rpc import from_xmlrpc_datetime tid = self.admin.ticket.create('test_update_time_changed', '...', {}) tid, created, modified, attrs = self.admin.ticket.get(tid) then = from_xmlrpc_datetime(modified) - datetime.timedelta(minutes=1) # Unrestricted old-style update (to be removed soon) try: self.admin.ticket.update(tid, "comment1", {'_ts': str(to_utimestamp(then))}) except Exception, e: self.assertTrue("Ticket has been updated since last get" in str(e)) # Update with 'action' to test new-style update. try: self.admin.ticket.update(tid, "comment1", {'_ts': str(to_utimestamp(then)), 'action': 'leave'}) except Exception, e: self.assertTrue("modified by someone else" in str(e)) self.admin.ticket.delete(tid) def test_update_time_same(self): # Unrestricted old-style update (to be removed soon) tid = self.admin.ticket.create('test_update_time_same', '...', {}) tid, created, modified, attrs = self.admin.ticket.get(tid) ts = attrs['_ts'] self.admin.ticket.update(tid, "comment1", {'_ts': ts}) self.admin.ticket.delete(tid) # Update with 'action' to test new-style update.
def update(self, req, id, comment, attributes={}, notify=False, author='', when=None): """ Update a ticket, returning the new ticket in the same form as get(). 'New-style' call requires two additional items in attributes: (1) 'action' for workflow support (including any supporting fields as retrieved by getActions()), (2) '_ts' changetime token for detecting update collisions (as received from get() or update() calls). ''Calling update without 'action' and '_ts' changetime token is deprecated, and will raise errors in a future version.'' """ t = model.Ticket(self.env, id) # custom author? if author and not (req.authname == 'anonymous' \ or 'TICKET_ADMIN' in req.perm(t.resource)): # only allow custom author if anonymous is permitted or user is admin self.log.warn( "RPC ticket.update: %r not allowed to change author " "to %r for comment on #%d", req.authname, author, id) author = '' author = author or req.authname # custom change timestamp? if when and not 'TICKET_ADMIN' in req.perm(t.resource): self.log.warn( "RPC ticket.update: %r not allowed to update #%d with " "non-current timestamp (%r)", author, id, when) when = None when = when or to_datetime(None, utc) # and action... if not 'action' in attributes: # FIXME: Old, non-restricted update - remove soon! self.log.warning("Rpc ticket.update for ticket %d by user %s " \ "has no workflow 'action'." % (id, req.authname)) req.perm(t.resource).require('TICKET_MODIFY') time_changed = attributes.pop('_ts', None) if time_changed and \ str(time_changed) != str(to_utimestamp(t.time_changed)): raise TracError("Ticket has been updated since last get().") for k, v in attributes.iteritems(): t[k] = v t.save_changes(author, comment, when=when) else: ts = TicketSystem(self.env) tm = TicketModule(self.env) # TODO: Deprecate update without time_changed timestamp time_changed = attributes.pop('_ts', to_utimestamp(t.time_changed)) try: time_changed = int(time_changed) except ValueError: raise TracError("RPC ticket.update: Wrong '_ts' token " \ "in attributes (%r)." % time_changed) action = attributes.get('action') avail_actions = ts.get_available_actions(req, t) if not action in avail_actions: raise TracError("Rpc: Ticket %d by %s " \ "invalid action '%s'" % (id, req.authname, action)) controllers = list(tm._get_action_controllers(req, t, action)) all_fields = [field['name'] for field in ts.get_ticket_fields()] for k, v in attributes.iteritems(): if k in all_fields and k != 'status': t[k] = v # TicketModule reads req.args - need to move things there... req.args.update(attributes) req.args['comment'] = comment # Collision detection: 0.11+0.12 timestamp req.args['ts'] = str(from_utimestamp(time_changed)) # Collision detection: 0.13/1.0+ timestamp req.args['view_time'] = str(time_changed) changes, problems = tm.get_ticket_changes(req, t, action) for warning in problems: add_warning(req, "Rpc ticket.update: %s" % warning) valid = problems and False or tm._validate_ticket(req, t) if not valid: raise TracError(" ".join( [warning for warning in req.chrome['warnings']])) else: tm._apply_ticket_changes(t, changes) self.log.debug("Rpc ticket.update save: %s" % repr(t.values)) t.save_changes(author, comment, when=when) # Apply workflow side-effects for controller in controllers: controller.apply_action_side_effects(req, t, action) if notify: try: tn = TicketNotifyEmail(self.env) tn.notify(t, newticket=False, modtime=when) except Exception, e: self.log.exception("Failure sending notification on change of " "ticket #%s: %s" % (t.id, e))
def get(self, req, id): """ Fetch a ticket. Returns [id, time_created, time_changed, attributes]. """ t = model.Ticket(self.env, id) req.perm(t.resource).require('TICKET_VIEW') t['_ts'] = str(to_utimestamp(t.time_changed)) return (t.id, t.time_created, t.time_changed, t.values)
def update(self, req, id, comment, attributes={}, notify=False, author='', when=None): """ Update a ticket, returning the new ticket in the same form as get(). 'New-style' call requires two additional items in attributes: (1) 'action' for workflow support (including any supporting fields as retrieved by getActions()), (2) '_ts' changetime token for detecting update collisions (as received from get() or update() calls). ''Calling update without 'action' and '_ts' changetime token is deprecated, and will raise errors in a future version.'' """ t = model.Ticket(self.env, id) # custom author? if author and not (req.authname == 'anonymous' \ or 'TICKET_ADMIN' in req.perm(t.resource)): # only allow custom author if anonymous is permitted or user is admin self.log.warn("RPC ticket.update: %r not allowed to change author " "to %r for comment on #%d", req.authname, author, id) author = '' author = author or req.authname # custom change timestamp? if when and not 'TICKET_ADMIN' in req.perm(t.resource): self.log.warn("RPC ticket.update: %r not allowed to update #%d with " "non-current timestamp (%r)", author, id, when) when = None when = when or to_datetime(None, utc) # and action... if not 'action' in attributes: # FIXME: Old, non-restricted update - remove soon! self.log.warning("Rpc ticket.update for ticket %d by user %s " \ "has no workflow 'action'." % (id, req.authname)) req.perm(t.resource).require('TICKET_MODIFY') time_changed = attributes.pop('_ts', None) if time_changed and \ str(time_changed) != str(to_utimestamp(t.time_changed)): raise TracError("Ticket has been updated since last get().") for k, v in attributes.iteritems(): t[k] = v t.save_changes(author, comment, when=when) else: ts = TicketSystem(self.env) tm = TicketModule(self.env) # TODO: Deprecate update without time_changed timestamp time_changed = attributes.pop('_ts', to_utimestamp(t.time_changed)) try: time_changed = int(time_changed) except ValueError: raise TracError("RPC ticket.update: Wrong '_ts' token " \ "in attributes (%r)." % time_changed) action = attributes.get('action') avail_actions = ts.get_available_actions(req, t) if not action in avail_actions: raise TracError("Rpc: Ticket %d by %s " \ "invalid action '%s'" % (id, req.authname, action)) controllers = list(tm._get_action_controllers(req, t, action)) all_fields = [field['name'] for field in ts.get_ticket_fields()] for k, v in attributes.iteritems(): if k in all_fields and k != 'status': t[k] = v # TicketModule reads req.args - need to move things there... req.args.update(attributes) req.args['comment'] = comment # Collision detection: 0.11+0.12 timestamp req.args['ts'] = str(from_utimestamp(time_changed)) # Collision detection: 0.13/1.0+ timestamp req.args['view_time'] = str(time_changed) changes, problems = tm.get_ticket_changes(req, t, action) for warning in problems: add_warning(req, "Rpc ticket.update: %s" % warning) valid = problems and False or tm._validate_ticket(req, t) if not valid: raise TracError( " ".join([warning for warning in req.chrome['warnings']])) else: tm._apply_ticket_changes(t, changes) self.log.debug("Rpc ticket.update save: %s" % repr(t.values)) t.save_changes(author, comment, when=when) # Apply workflow side-effects for controller in controllers: controller.apply_action_side_effects(req, t, action) if notify: try: tn = TicketNotifyEmail(self.env) tn.notify(t, newticket=False, modtime=when) except Exception, e: self.log.exception("Failure sending notification on change of " "ticket #%s: %s" % (t.id, e))