def apply_action_side_effects(self, req, ticket, action): config = self.config['ticket-workflow'] if config.get(action + '.earn_value', '') != '': value = 0 time = to_utimestamp(datetime_now(FixedOffset(0, 'UTC'))) try: evdef = config.get(action + '.earn_value', '').strip() if evdef.isdigit(): value = float(evdef) elif evdef.endswith('%') and 'activity_earn_value' in ticket: value = float(ticket['activity_earn_value']) * float(evdef[:-1]) / 100 except Exception as e: self.log.warning(e) with self.env.db_transaction as db: cursor = db.cursor() cursor.execute(""" INSERT INTO earn_value VALUES (%s, %s, %s, %s, %s, %s, %s); """, (ticket.id, 'workflow', action, value, req.authname, time, 0)) if config.get(action + '.update_time', '') != '': field = config.get(action + '.update_time').strip() if field in ticket: ticket[field] = datetime_now(FixedOffset(0, 'UTC')) ticket.save_changes()
def test_version_release_date_displayed(self): """Version release date is shown in ticket properties.""" v1 = Version(self.env) v1.name = 'v1' v1.time = datetime_now(utc) - timedelta(weeks=2) v1.insert() v2 = Version(self.env) v2.name = 'v2' v2.insert() ticket = [self._insert_ticket(summary='ticket 1', version='v1'), self._insert_ticket(summary='ticket 2', version='v2'), self._insert_ticket(summary='ticket 3', version='v3')] def version_field(data): for field in data['fields']: if field['name'] == 'version': return field # Version with release data. req = MockRequest(self.env, method='GET', args={'id': ticket[0].id}) data = self.ticket_module.process_request(req)[1] self.assertIn(u'title="Released ', unicode(version_field(data)['rendered'])) # Version without release data. req = MockRequest(self.env, method='GET', args={'id': ticket[1].id}) data = self.ticket_module.process_request(req)[1] self.assertNotIn(u'title="Released ', unicode(version_field(data)['rendered'])) # Non-existent version. req = MockRequest(self.env, method='GET', args={'id': ticket[2].id}) data = self.ticket_module.process_request(req)[1] self.assertNotIn(u'title="Released ', unicode(version_field(data)['rendered']))
def process_action(self, msg, author, githash=None): self.env.log.debug('process_action') # Find all the #123 strings in the commit message. ticket_re = re.compile('#[0-9]+') ticket_numbers = ticket_re.findall(msg) # Turn the ticket numbers into ints. ticket_numbers = set( [int(ticket_number[1:]) for ticket_number in ticket_numbers]) # For each ticket date = datetime_now(utc) for ticket_number in ticket_numbers: self.env.log.debug( 'Found ticket number: {n}'.format(n=str(ticket_number))) if (githash is not None and self._githash_storecheck(ticket_number, githash)): continue try: db = self.env.get_db_cnx() ticket = Ticket(self.env, int(ticket_number), db) ticket.save_changes(author, msg, date) db.commit() self._notify(ticket, date) self.env.log.debug('Comment added') except ResourceNotFound, e: self.log.error( 'Ticket not found: {n}'.format(n=str(ticket_number))) continue
def test_template_data_changes_for_time_field(self): self.env.config.set('ticket-custom', 'timefield', 'time') dt1 = datetime(2015, 7, 8, tzinfo=utc) dt2 = datetime(2015, 12, 11, tzinfo=utc) with self.env.db_transaction: self._insert_ticket(summary='Time fields', timefield=datetime_now(utc)) self.env.db_transaction("UPDATE ticket_custom SET value='invalid' " "WHERE ticket=1 AND name='timefield'") t = Ticket(self.env, 1) t['timefield'] = dt1 t.save_changes('anonymous') t = Ticket(self.env, 1) t['timefield'] = dt2 t.save_changes('anonymous') req = MockRequest(self.env, method='GET', path_info='/ticket/1') self.assertTrue(self.ticket_module.match_request(req)) data = self.ticket_module.process_request(req)[1] changes = data['changes'] dt1_text = user_time(req, format_datetime, dt1) dt2_text = user_time(req, format_datetime, dt2) self.assertEqual(2, len(changes)) self.assertEqual('', changes[0]['fields']['timefield']['old']) self.assertEqual(dt1_text, changes[0]['fields']['timefield']['new']) self.assertEqual(dt1_text, changes[1]['fields']['timefield']['old']) self.assertEqual(dt2_text, changes[1]['fields']['timefield']['new'])
def add_pages(tc, names): for name in names: now = datetime_now(utc) w = WikiPage(tc.env) w.name = name w.text = '--' w.save('joe', 'the page ' + name, now)
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)
def test_absolute(self): t = datetime_now(utc) - timedelta(days=1) label = 'on %s at %s' % \ (format_date(t, locale=locale_en, tzinfo=utc), format_time(t, locale=locale_en, tzinfo=utc)) self.assertEqual(label, self._format_chrome(t, 'absolute', False)) self.assertEqual(label, self._format_timeline(t, 'absolute', False))
def test_export_ical_from_roadmap(self): self.insert_milestone('milestone1', datetime_now(utc)) self.insert_milestone('milestone2') rm = RoadmapModule(self.env) req = MockRequest(self.env, path_info='/roadmap', args={'format': 'ics'}) self.assertTrue(rm.match_request(req)) with self.assertRaises(RequestDone): rm.process_request(req) self.assertEqual('200 Ok', req.status_sent[0]) self.assertRegexpMatches( req.response_sent.getvalue(), """\ BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Edgewall Software//NONSGML Trac [\.\w]+//EN METHOD:PUBLISH X-WR-CALNAME:My Project - Roadmap X-WR-CALDESC:My example project X-WR-TIMEZONE:UTC BEGIN:VEVENT UID:</trac.cgi/milestone/[email protected]/trac.cgi> DTSTAMP:\w+ DTSTART;VALUE=DATE:\w+ SUMMARY:Milestone milestone1 URL:http://example.org/trac.cgi/milestone/milestone1 END:VEVENT END:VCALENDAR """.replace('\n', '\r\n'))
def runTest(self): """Admin milestone duedate""" name = "DueMilestone" duedate = datetime_now(tz=utc) duedate_string = format_datetime(duedate, tzinfo=utc, locale=locale_en) self._tester.create_milestone(name, due=duedate_string) tc.find(duedate_string)
def _notify_event(self, text, category='created', time=None, author=None): self.sender.history[:] = () event = TestNotificationEvent('test', category, TestModel(text), time or datetime_now(utc), author=author) self.notsys.notify(event)
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']))
def test_timeline_events(self): """Regression test for #11288""" req1 = MockRequest(self.env) tktmod = web_ui.TicketModule(self.env) now = datetime_now(utc) start = now - timedelta(hours=1) stop = now + timedelta(hours=1) events = tktmod.get_timeline_events(req1, start, stop, ['ticket_details']) self.assertTrue(all(ev[0] != 'batchmodify' for ev in events)) prio_ids = {} for i in xrange(20): priority = ('', 'minor', 'major', 'critical')[i % 4] t = insert_ticket(self.env, summary='Ticket %d' % i, priority=priority) prio_ids.setdefault(t['priority'], []).append(t.id) tktids = prio_ids['critical'] + prio_ids['major'] + \ prio_ids['minor'] + prio_ids[''] req2 = MockRequest(self.env, method='POST', authname='has_ta_&_bm', path_info='/batchmodify', args={ 'batchmod_value_summary': 'batch updated ticket', 'batchmod_value_owner': 'ticket11288', 'batchmod_value_reporter': 'ticket11288', 'action': 'leave', 'selected_tickets': ','.join(str(t) for t in tktids), }) batch = BatchModifyModule(self.env) self.assertTrue(batch.match_request(req2)) with self.assertRaises(RequestDone): batch.process_request(req2) # shuffle ticket_change records with self.env.db_transaction as db: rows = db('SELECT * FROM ticket_change') db.execute('DELETE FROM ticket_change') rows = rows[0::4] + rows[1::4] + rows[2::4] + rows[3::4] db.executemany('INSERT INTO ticket_change VALUES (%s)' % ','.join(('%s',) * len(rows[0])), rows) events = tktmod.get_timeline_events(req1, start, stop, ['ticket_details']) events = [ev for ev in events if ev[0] == 'batchmodify'] self.assertEqual(1, len(events)) batch_ev = events[0] self.assertEqual('has_ta_&_bm', batch_ev[2]) self.assertEqual(tktids, batch_ev[3][0]) self.assertEqual('updated', batch_ev[3][1]) context = web_context(req2) self.assertEqual(req2.href.query(id=','.join(str(t) for t in tktids)), tktmod.render_timeline_event(context, 'url', batch_ev))
def setUp(self): self.env = EnvironmentStub() self.mmodule = MilestoneModule(self.env) self.terms = ['MilestoneAlpha', 'MilestoneBeta', 'MilestoneGamma'] for term in self.terms + [' '.join(self.terms)]: m = Milestone(self.env) m.name = term m.due = datetime_now(utc) m.description = random_sentence() m.insert()
def get_default_due(self, req): """Returns a `datetime` object representing the default due date in the user's timezone. The default due time is 18:00 in the user's time zone. """ now = datetime_now(req.tz) default_due = datetime(now.year, now.month, now.day, 18) if now.hour > 18: default_due += timedelta(days=1) return to_datetime(default_due, req.tz)
def update_priority(self, db=None): with self.env.db_transaction as db: cursor = db.cursor() now = to_utimestamp(datetime_now(utc)) cursor.execute(""" UPDATE subscription SET changetime=%s, priority=%s WHERE id=%s """, (now, int(self.values['priority']), self.values['id']))
def changeset_modified(self, repos, changeset, old_changeset): if self._is_duplicate(changeset): return tickets = self._parse_message(changeset.message) old_tickets = {} if old_changeset is not None: old_tickets = self._parse_message(old_changeset.message) tickets = dict(each for each in tickets.iteritems() if each[0] not in old_tickets) comment = self.make_ticket_comment(repos, changeset) self._update_tickets(tickets, changeset, comment, datetime_now(utc))
def _save_ticket_changes(self, req, selected_tickets, new_values, comment, action): """Save changes to tickets.""" valid = True for manipulator in self.ticket_manipulators: if hasattr(manipulator, 'validate_comment'): for message in manipulator.validate_comment(req, comment): valid = False add_warning(req, tag_("The ticket %(field)s is invalid: " "%(message)s", field=tag.strong(_('comment')), message=message)) tickets = [] for id_ in selected_tickets: t = Ticket(self.env, id_) values = self._get_updated_ticket_values(req, t, new_values) for ctlr in self._get_action_controllers(req, t, action): values.update(ctlr.get_ticket_changes(req, t, action)) t.populate(values) for manipulator in self.ticket_manipulators: for field, message in manipulator.validate_ticket(req, t): valid = False if field: add_warning(req, tag_("The ticket field %(field)s is " "invalid: %(message)s", field=tag.strong(field), message=message)) else: add_warning(req, message) tickets.append(t) if not valid: return when = datetime_now(utc) with self.env.db_transaction: for t in tickets: t.save_changes(req.authname, comment, when=when) for ctlr in self._get_action_controllers(req, t, action): ctlr.apply_action_side_effects(req, t, action) event = BatchTicketChangeEvent(selected_tickets, when, req.authname, comment, new_values, action) try: NotificationSystem(self.env).notify(event) except Exception as e: self.log.error("Failure sending notification on ticket batch" "change: %s", exception_to_unicode(e)) add_warning(req, tag_("The changes have been saved, but an error " "occurred while sending notifications: " "%(message)s", message=to_unicode(e)))
def test(self, req, author, content, ip): threshold = datetime_now() - timedelta(hours=1) num_posts = 0 for entry in LogEntry.select(self.env, ipnr=ip): if datetime.fromtimestamp(entry.time) < threshold: break num_posts += 1 if num_posts > self.max_posts: return -abs(self.karma_points) * num_posts / self.max_posts, \ N_("Maximum number of posts per hour for this IP exceeded")
def setUp(self): self.env = self._create_env() # TODO: remove the following lines in order to discover # all the places were we should use the req.href # instead of env.href self.env.href = self.req.href self.env.abs_href = self.req.abs_href wiki = WikiPage(self.env) wiki.name = 'WikiStart' wiki.text = '--' wiki.save('joe', 'Entry page', '::1', datetime_now(utc)) if self._setup: self._setup(self)
def changeset_modified(self, repos, changeset, old_changeset): self.log.debug("changeset_modified on %s for changesets %s", repos.name, changeset.rev) if self._is_duplicate(changeset): return tickets = self._parse_message(changeset.message) old_tickets = {} if old_changeset is not None: old_tickets = self._parse_message(old_changeset.message) tickets = dict(each for each in tickets.iteritems() if each[0] not in old_tickets) comment = self.make_ticket_comment(repos, changeset) self._update_tickets(tickets, changeset, comment, datetime_now(utc))
def _iso8601_to_ts(s): """Parse ISO-8601 string to microsecond POSIX timestamp.""" try: s = str(s) if s.isnumeric(): # Valid type, no conversion required. return long(s) tm = time.strptime(s, '%Y-%m-%d %H:%M:%S') dt = datetime.datetime(*(tm[0:6] + (0, utc))) return to_utimestamp(dt) except (AttributeError, TypeError, ValueError): # Create a valid timestamp anyway. return to_utimestamp(datetime_now(utc))
def save(self, author, comment, remote_addr=None, t=None): """Save a new version of a page. :since 1.0.3: `remote_addr` is optional and deprecated, and will be removed in 1.3.1 """ if not validate_page_name(self.name): raise TracError( _("Invalid Wiki page name '%(name)s'", name=self.name)) new_text = self.text != self.old_text if not new_text and self.readonly == self.old_readonly: raise TracError(_("Page not modified")) t = t or datetime_now(utc) with self.env.db_transaction as db: if new_text: db( """INSERT INTO wiki (name, version, time, author, ipnr, text, comment, readonly) VALUES (%s,%s,%s,%s,%s,%s,%s,%s) """, (self.name, self.version + 1, to_utimestamp(t), author, remote_addr, self.text, comment, self.readonly)) self.version += 1 else: db("UPDATE wiki SET readonly=%s WHERE name=%s", (self.readonly, self.name)) if self.version == 1: # Invalidate page name cache del WikiSystem(self.env).pages self.author = author self.comment = comment self.time = t for listener in WikiSystem(self.env).change_listeners: if self.version == 1: listener.wiki_page_added(self) else: from trac.util import arity if arity(listener.wiki_page_changed) == 6: listener.wiki_page_changed(self, self.version, t, comment, author, remote_addr) else: listener.wiki_page_changed(self, self.version, t, comment, author) self.old_readonly = self.readonly self.old_text = self.text
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.itervalues(): 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.itervalues(): delete_ids.extend(ids) if delete_ids: db( "DELETE FROM notify_subscription WHERE id IN (%s)" % ','.join(('%s', ) * len(delete_ids)), delete_ids)
def insert(self, filename, fileobj, size, t=None): """Create a new Attachment record and save the file content. """ self.size = int(size) if size else 0 self.filename = None if t is None: t = datetime_now(utc) elif not isinstance(t, datetime): # Compatibility with 0.11 t = to_datetime(t, utc) self.date = t parent_resource = Resource(self.parent_realm, self.parent_id) if not resource_exists(self.env, parent_resource): raise ResourceNotFound( _("%(parent)s doesn't exist, can't create attachment", parent=get_resource_name(self.env, parent_resource))) # Make sure the path to the attachment is inside the environment # attachments directory attachments_dir = os.path.join(os.path.normpath(self.env.path), 'files', 'attachments') dir = self.path commonprefix = os.path.commonprefix([attachments_dir, dir]) if commonprefix != attachments_dir: raise TracError( _( 'Cannot create attachment "%(att)s" as ' '%(realm)s:%(id)s is invalid', att=filename, realm=self.parent_realm, id=self.parent_id)) if not os.access(dir, os.F_OK): os.makedirs(dir) filename, targetfile = self._create_unique_file(dir, filename) with targetfile: with self.env.db_transaction as db: db("INSERT INTO attachment VALUES (%s,%s,%s,%s,%s,%s,%s,%s)", (self.parent_realm, self.parent_id, filename, self.size, to_utimestamp(t), self.description, self.author, self.ipnr)) shutil.copyfileobj(fileobj, targetfile) self.filename = filename self.env.log.info("New attachment: %s by %s", self.title, self.author) for listener in AttachmentModule(self.env).change_listeners: listener.attachment_added(self)
def test_timeline_events(self): """Regression test for #11288""" tktmod = web_ui.TicketModule(self.env) now = datetime_now(utc) start = now - timedelta(hours=1) stop = now + timedelta(hours=1) events = tktmod.get_timeline_events(self.req, start, stop, ['ticket_details']) self.assertEqual(True, all(ev[0] != 'batchmodify' for ev in events)) prio_ids = {} for i in xrange(20): t = Ticket(self.env) t['summary'] = 'Ticket %d' % i t['priority'] = ('', 'minor', 'major', 'critical')[i % 4] tktid = t.insert() prio_ids.setdefault(t['priority'], []).append(tktid) tktids = prio_ids['critical'] + prio_ids['major'] + \ prio_ids['minor'] + prio_ids[''] new_values = { 'summary': 'batch updated ticket', 'owner': 'ticket11288', 'reporter': 'ticket11288' } batch = BatchModifyModule(self.env) batch._save_ticket_changes(self.req, tktids, new_values, '', 'leave') # shuffle ticket_change records with self.env.db_transaction as db: rows = db('SELECT * FROM ticket_change') db.execute('DELETE FROM ticket_change') rows = rows[0::4] + rows[1::4] + rows[2::4] + rows[3::4] db.executemany( 'INSERT INTO ticket_change VALUES (%s)' % ','.join( ('%s', ) * len(rows[0])), rows) events = tktmod.get_timeline_events(self.req, start, stop, ['ticket_details']) events = [ev for ev in events if ev[0] == 'batchmodify'] self.assertEqual(1, len(events)) batch_ev = events[0] self.assertEqual('anonymous', batch_ev[2]) self.assertEqual(tktids, batch_ev[3][0]) self.assertEqual('updated', batch_ev[3][1]) context = web_context(self.req) self.assertEqual( self.req.href.query(id=','.join(str(t) for t in tktids)), tktmod.render_timeline_event(context, 'url', batch_ev))
def runTest(self): name = self._testenv.add_milestone() tid1 = self._tester.create_ticket(info={'milestone': name}) tc.click('#propertyform .collapsed .foldable a') tc.formvalue('propertyform', 'action', 'resolve') tc.formvalue('propertyform', 'action_resolve_resolve_resolution', 'fixed') tc.submit('submit') # Check that hint is shown when there are no open tickets to retarget milestone_url = self._tester.url + "/admin/ticket/milestones/" + name self._tester.go_to_url(milestone_url) tc.find("There are no open tickets associated with this milestone.") retarget_to = self._testenv.add_milestone() # Check that open tickets retargeted, closed not retargeted tid2 = self._tester.create_ticket(info={'milestone': name}) self._tester.go_to_url(milestone_url) completed = format_datetime(datetime_now(tz=utc) - datetime.timedelta(hours=1), tzinfo=localtz, locale=locale_en) tc.formvalue('edit', 'completed', True) tc.formvalue('edit', 'completeddate', completed) tc.formvalue('edit', 'target', retarget_to) tc.submit('save') tc.url(self._tester.url + '/admin/ticket/milestones') tc.find('The open tickets associated with milestone "%s" ' 'have been retargeted to milestone "%s".' % (name, retarget_to)) tc.find("Completed") # Closed ticket will not be retargeted. self._tester.go_to_ticket(tid1) tc.find('<a class="closed milestone" href="/milestone/%(name)s" ' 'title="Completed .+ ago (.+)">%(name)s</a>' % {'name': name}) tc.notfind('changed from <em>%s</em> to <em>%s</em>' % (name, retarget_to)) tc.notfind("Ticket retargeted after milestone closed") # Open ticket will be retargeted. self._tester.go_to_ticket(tid2) tc.find('<a class="milestone" href="/milestone/%(name)s" ' 'title="No date set">%(name)s</a>' % {'name': retarget_to}) tc.find('<span class="trac-field-old">%s</span>' '[ \n]+→[ \n]+' '<span class="trac-field-new">%s</span>' % (name, retarget_to)) tc.find("Ticket retargeted after milestone closed")
def import_page(self, filename, title, create_only=[], replace=False): if not validate_page_name(title): raise AdminCommandError( _("Invalid Wiki page name '%(name)s'", name=title)) if filename: if not os.path.isfile(filename): raise AdminCommandError( _("'%(name)s' is not a file", name=path_to_unicode(filename))) data = read_file(filename) else: data = sys.stdin.read() data = to_unicode(data, 'utf-8') with self.env.db_transaction as db: # Make sure we don't insert the exact same page twice old = db( """SELECT text FROM wiki WHERE name=%s ORDER BY version DESC LIMIT 1 """, (title, )) if old and title in create_only: printout(_(" %(title)s already exists", title=title)) return False if old and data == old[0][0]: printout(_(" %(title)s is already up to date", title=title)) return False if replace and old: db( """UPDATE wiki SET text=%s WHERE name=%s AND version=(SELECT max(version) FROM wiki WHERE name=%s) """, (data, title, title)) else: db( """INSERT INTO wiki (version, readonly, name, time, author, text) SELECT 1 + COALESCE(max(version), 0), COALESCE(max(readonly), 0), %s, %s, 'trac', %s FROM wiki WHERE name=%s AND version=(SELECT max(version) FROM wiki WHERE name=%s) """, (title, to_utimestamp( datetime_now(utc)), data, title, title)) if not old: del WikiSystem(self.env).pages return True
def runTest(self): """Admin modify milestone duedate on detail page""" name = self._testenv.add_milestone() # Modify the details of the milestone milestone_url = self._tester.url + "/admin/ticket/milestones" self._tester.go_to_url(milestone_url) tc.follow(name) tc.url(milestone_url + '/' + name, regexp=False) duedate = datetime_now(tz=utc) duedate_string = format_datetime(duedate, tzinfo=utc, locale=locale_en) tc.formvalue('edit', 'due', True) tc.formvalue('edit', 'duedate', duedate_string) tc.submit('save') tc.url(milestone_url, regexp=False) tc.find(name + '(<[^>]*>|\\s)*' + duedate_string, 's')
def runTest(self): """Admin milestone completed in the future""" name = self._testenv.add_milestone() milestone_url = self._tester.url + "/admin/ticket/milestones" self._tester.go_to_url(milestone_url) tc.follow(name) tc.url(milestone_url + '/' + name, regexp=False) tc.formvalue('edit', 'completed', True) cdate = datetime_now(tz=utc) + datetime.timedelta(days=2) cdate_string = format_date(cdate, tzinfo=localtz, locale=locale_en) tc.formvalue('edit', 'completeddate', cdate_string) tc.submit('save') tc.find('Completion date may not be in the future') # And make sure it wasn't marked as completed. self._tester.go_to_roadmap() tc.find(name)
def pretty_dateinfo(date, format=None, dateonly=False): if not date: return '' if format == 'date': absolute = user_time(req, format_date, date) else: absolute = user_time(req, format_datetime, date) now = datetime_now(localtz) relative = pretty_timedelta(date, now) if not format: format = req.session.get( 'dateinfo', Chrome(self.env).default_dateinfo_format) if format == 'relative': if date > now: label = _("in %(relative)s", relative=relative) \ if not dateonly else relative title = _("on %(date)s at %(time)s", date=user_time(req, format_date, date), time=user_time(req, format_time, date)) return tag.span(label, title=title) else: label = _("%(relative)s ago", relative=relative) \ if not dateonly else relative title = _("See timeline at %(absolutetime)s", absolutetime=absolute) else: if dateonly: label = absolute elif req.lc_time == 'iso8601': label = _("at %(iso8601)s", iso8601=absolute) elif format == 'date': label = _("on %(date)s", date=absolute) else: label = _("on %(date)s at %(time)s", date=user_time(req, format_date, date), time=user_time(req, format_time, date)) if date > now: title = _("in %(relative)s", relative=relative) return tag.span(label, title=title) title = _("See timeline %(relativetime)s ago", relativetime=relative) return self.get_timeline_link(req, date, label, precision='second', title=title)
def save(self, author, comment, t=None, replace=False): """Save a new version of a page.""" if not validate_page_name(self.name): raise TracError( _("Invalid Wiki page name '%(name)s'", name=self.name)) new_text = self.text != self.old_text if not new_text and self.readonly == self.old_readonly: raise TracError(_("Page not modified")) t = t or datetime_now(utc) with self.env.db_transaction as db: if new_text: if replace and self.version != 0: db( """ UPDATE wiki SET text=%s WHERE name=%s AND version=%s """, (self.text, self.name, self.version)) else: self.version += 1 db( """INSERT INTO wiki (name,version,time,author,text,comment,readonly) VALUES (%s,%s,%s,%s,%s,%s,%s) """, (self.name, self.version, to_utimestamp(t), author, self.text, comment, self.readonly)) else: db("UPDATE wiki SET readonly=%s WHERE name=%s", (self.readonly, self.name)) if self.version == 1: # Invalidate page name cache del WikiSystem(self.env).pages self.author = author self.comment = comment self.time = t for listener in WikiSystem(self.env).change_listeners: with self.env.component_guard(listener): if self.version == 1: listener.wiki_page_added(self) else: listener.wiki_page_changed(self, self.version, t, comment, author) self.old_readonly = self.readonly self.old_text = self.text
def setUp(self): self.env = EnvironmentStub(default_data=True) self.mmodule = MilestoneModule(self.env) self.terms = ['MilestoneAlpha', 'MilestoneBeta', 'MilestoneGamma'] for term in self.terms + [' '.join(self.terms)]: m = Milestone(self.env) m.name = term m.due = datetime_now(utc) m.description = u"""\ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod \ tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim \ veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea \ commodo consequat. Duis aute irure dolor in reprehenderit in voluptate \ velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat \ cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id \ est laborum.""" m.insert()
def notify_attachment(self, ticket, attachment, added=True): """Send ticket attachment notification (untranslated)""" self.ticket = ticket self.modtime = attachment.date or datetime_now(utc) self.newticket = False self.reporter = '' self.owner = '' link = self.env.abs_href.ticket(ticket.id) summary = self.ticket['summary'] author = attachment.author # Note: no translation yet changes_body = wrap( " * Attachment \"%s\" %s." % (attachment.filename, "added" if added else "removed"), self.COLS, ' ', ' ', '\n', self.ambiwidth) + "\n" if attachment.description: changes_body += "\n" + wrap(attachment.description, self.COLS, ' ', ' ', '\n', self.ambiwidth) ticket_values = ticket.values.copy() ticket_values['id'] = ticket.id ticket_values['description'] = wrap(ticket_values.get( 'description', ''), self.COLS, initial_indent=' ', subsequent_indent=' ', linesep='\n', ambiwidth=self.ambiwidth) ticket_values['new'] = self.newticket ticket_values['link'] = link subject = self.format_subj(summary, False) with _translation_deactivated(ticket): self.data.update({ 'ticket_props': self.format_props(), 'ticket_body_hdr': self.format_hdr(), 'subject': subject, 'ticket': ticket_values, 'changes_body': changes_body, 'changes_descr': '', 'change': { 'author': self.obfuscate_email(author) }, }) super(TicketNotifyEmail, self).notify(ticket.id, subject, author)
def add(cls, env, subscription, db=None): """ID and priority get overwritten.""" with env.db_transaction as db: cursor = db.cursor() priority = len(cls.find_by_sid_and_distributor( env, subscription['sid'], subscription['authenticated'], subscription['distributor'], db)) + 1 now = to_utimestamp(datetime_now(utc)) cursor.execute(""" INSERT INTO subscription (time,changetime,sid,authenticated, distributor,format,priority,adverb,class) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s) """, (now, now, subscription['sid'], subscription['authenticated'], subscription['distributor'], subscription['format'], int(priority), subscription['adverb'], subscription['class']))
def purge(cls, env, days): """Delete log entries older than the specified number of days.""" threshold = datetime_now() - timedelta(days=days) env.db_transaction(""" DELETE FROM spamfilter_log WHERE time < %s """, (mktime(threshold.timetuple()),))
def render_preference_panel(self, req, panel): """ Implement IPreferencePanelProvider.render_preference_panel method Add request handler for accesstoken POST request """ add_stylesheet(req, PACKAGE + '/css/advsearch.css') add_script(req, PACKAGE + '/js/hat.js') add_script(req, PACKAGE + '/js/advsearch.js') self.log.debug(req.method); content_type = req.get_header('Content-Type') or 'application/json' new_token = [] action = req.args.get('action') token_id = req.args.get('token_id') if req.args.get('tokens'): new_token = json.loads(req.args.get('tokens')) if action == 'POST': if content_type == 'text/html': new_token = req.args.get('tokens') if new_token: tokens = json.loads(new_token) with self.env.db_transaction as db: for t in tokens: db( "INSERT INTO kkbox_trac_access_token (" "username, access_token, description, change_time, create_time) " "VALUES (%s,%s,%s,%s,%s)", (req.perm.username, hashlib.sha224(t['accessToken']).hexdigest(), t['description'], datetime_now(utc), datetime_now(utc))) self.env.log.info("New access token for %s", req.perm.username) add_notice(req, _('Your access tokens have been saved.')) else: # Read request body #content_len = int(req.get_header('content-length') or 0) new_token = { 'accessToken': req.args.get('accessToken'), 'description': req.args.get('description'), } if new_token: with self.env.db_transaction as db: c = db.cursor() t = new_token db( "INSERT INTO kkbox_trac_access_token (" "username, access_token, description, change_time, create_time) " "VALUES (%s,%s,%s,%s,%s)", (req.perm.username, hashlib.sha224(t['accessToken']).hexdigest(), t['description'], datetime_now(utc), datetime_now(utc))) self.env.log.info("New access token for %s at %s" % (req.perm.username, datetime_now(utc))) elif action == 'DELETE': if 'token_id' in req.query_string: with self.env.db_transaction as db: db("""DELETE FROM kkbox_trac_access_token WHERE id=%s""", (token_id,)) self.env.log.info("Delete access token id=%s", token_id) elif action == 'PUT': if 'token_id' in req.query_string: body = req.args.get('description') change_time = datetime_now(utc) with self.env.db_transaction as db: db("""UPDATE kkbox_trac_access_token SET description=%s, change_time=%s WHERE id=%s""", (body, change_time, token_id, )) self.env.log.info("Update access token for %s id=%s at %s" % (req.perm.username, token_id, change_time)) else: def _from_database(id_, access_token, description_, create_time): return { 'id': id_, 'accessToken': access_token, 'description': description_, 'creationTime': create_time } for row in self.env.db_query(""" SELECT id AS id_, access_token, description, create_time FROM kkbox_trac_access_token WHERE username=%s ORDER BY create_time DESC """, (req.perm.username,)): new_token.append(_from_database(*row)) new_token = json.dumps(new_token) self.env.log.info("New access token for %s", new_token) return 'prefs_tokens.html', { 'tokens': new_token }
def changeset_added_impl(self, repos, changeset): tickets = self._parse_message(changeset.message) comment = self.make_ticket_comment(repos, changeset) return self._update_tickets(tickets, changeset, comment, datetime_now(utc))