Пример #1
0
    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()
Пример #2
0
    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
Пример #4
0
    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'])
Пример #5
0
 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)
Пример #6
0
 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)
Пример #7
0
 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))
Пример #8
0
    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'))
Пример #9
0
 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)
Пример #10
0
 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)
Пример #11
0
 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']))
Пример #12
0
    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))
Пример #13
0
 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()
Пример #14
0
 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)
Пример #15
0
    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']))
Пример #16
0
 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))
Пример #17
0
    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)))
Пример #18
0
    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")
Пример #19
0
 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))
Пример #21
0
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))
Пример #22
0
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))
Пример #23
0
    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
Пример #24
0
    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)
Пример #25
0
    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)
Пример #26
0
    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))
Пример #27
0
    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")
Пример #28
0
    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
Пример #29
0
    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')
Пример #30
0
 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)
Пример #31
0
 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)
Пример #32
0
    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
Пример #33
0
    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()
Пример #34
0
    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)
Пример #35
0
    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']))
Пример #36
0
 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()),))
Пример #37
0
    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))