예제 #1
0
 def _process_request(self, req):
     mod = TimelineModule(self.env)
     req = MockRequest(self.env,
                       path_info='/timeline',
                       args={'format': 'rss'})
     self.assertTrue(mod.match_request(req))
     return mod.process_request(req)
예제 #2
0
    def _get_timeline_events(self, tracenviron):
        """
        Get the timeline events for one project, based on the given in trac
        environment object.
        """

        # skip empty projects
        if tracenviron is None:
            return None

        daterange = int(self.conf.activity_calculation_daterange)

        # end time of timeline is current time
        todate = datetime.now(datefmt.localtz)

        # start time of timeline is last update of if not known, last two monts
        fromdate = todate
        fromdate = fromdate - timedelta(days=daterange)

        stop = todate
        start = fromdate

        # events will continue the timeline events
        events = []

        # Access event providers for timeline events
        event_providers = TimelineModule(tracenviron).event_providers

        # create a dummy request (see class above)
        req = DummyReq('user', 'password', 'method', 'uri', 'args')
        req.permissions = ('TICKET_VIEW', 'CHANGESET_VIEW', 'WIKI_VIEW', 'ATTACHMENT_VIEW', 'DISCUSSION_VIEW')
        req.authname = 'authname'

        # filters will contain the available timeline event types
        available_filters = []
        for event_provider in event_providers:
            available_filters += event_provider.get_timeline_filters(req) or []

        filters = []
        # check the request or session for enabled filters, or use default
        for test in (lambda f: f[0] in req.args,
                     lambda f: len(f) == 2 or f[2]):
            if filters:
                break
            filters = [f[0] for f in available_filters if test(f)]

        # do the actual event querying
        for provider in event_providers:
            # note: this is because if discussion is not in current project, do not fail
            try:
                for event in provider.get_timeline_events(req, start, stop, filters):
                    events.append(self._event_data(provider, event))
            except Exception:
                self.conf.log.exception(
                    "ActivityCalculator.get_timeline_events(%s) couldn't get timeline events from %s." %
                    (tracenviron.project_name, str(provider)))

        events.sort(lambda x, y: cmp(y['date'], x['date']))

        return events
예제 #3
0
 def _format_timeline(self, d, format, dateonly):
     data = Chrome(self.env).populate_data(self.req, {})
     TimelineModule(self.env) \
         .post_process_request(self.req, 'timeline.html', data, None)
     return plaintext(data['pretty_dateinfo'](d,
                                              format=format,
                                              dateonly=dateonly))
 def _create_html_body(self, chrome, req, ticket, cnum, link):
     tktmod = TicketModule(self.env)
     attmod = AttachmentModule(self.env)
     data = tktmod._prepare_data(req, ticket)
     tktmod._insert_ticket_data(req, ticket, data, req.authname, {})
     data['ticket']['link'] = link
     changes = data.get('changes')
     if cnum is None:
         changes = []
     else:
         changes = [change for change in (changes or [])
                           if change.get('cnum') == cnum]
     data['changes'] = changes
     context = Context.from_request(req, ticket.resource, absurls=True)
     alist = attmod.attachment_data(context)
     alist['can_create'] = False
     data.update({
             'can_append': False,
             'show_editor': False,
             'start_time': ticket['changetime'],
             'context': context,
             'alist': alist,
             'styles': self._get_styles(chrome),
             'link': tag.a(link, href=link),
             'tag_': tag_,
            })
     template = 'htmlnotification_ticket.html'
     # use pretty_dateinfo in TimelineModule
     TimelineModule(self.env).post_process_request(req, template, data,
                                                   None)
     rendered = chrome.render_template(req, template, data, fragment=True)
     return unicode(rendered)
예제 #5
0
    def test_daysback_less_than_min(self):
        """Daysback minimum value is 1."""
        req = MockRequest(self.env, args={'daysback': '-1'})

        data = TimelineModule(self.env).process_request(req)[1]

        self.assertEqual(1, data['daysback'])
예제 #6
0
    def test_daysback_greater_than_max(self):
        """Daysback is limited to [timeline] max_daysback."""
        req = MockRequest(self.env, args={'daysback': '100'})

        data = TimelineModule(self.env).process_request(req)[1]

        self.assertEqual(90, data['daysback'])
예제 #7
0
    def test_daysback_invalid_default_is_used(self):
        """Daysback request value is invalid: default value is used."""
        req = MockRequest(self.env, args={'daysback': '--'})

        data = TimelineModule(self.env).process_request(req)[1]

        self.assertEqual(30, data['daysback'])
예제 #8
0
    def test_daysback_from_session(self):
        """Daysback value is retrieved from session attributes."""
        PermissionSystem(self.env).grant_permission('user1', 'TIMELINE_VIEW')
        req = MockRequest(self.env, authname='user1')
        req.session.set('timeline.daysback', '45')

        data = TimelineModule(self.env).process_request(req)[1]

        self.assertEqual(45, data['daysback'])
예제 #9
0
    def test_daysback_default_is_90_for_rss_format(self):
        """Daysback default is 90 for RSS format request."""
        PermissionSystem(self.env).grant_permission('user1', 'TIMELINE_VIEW')
        req = MockRequest(self.env, authname='user1', args={'format': 'rss'})
        req.session.set('timeline.daysback', '45')

        data = TimelineModule(self.env).process_request(req)[1]

        self.assertEqual(90, data['daysback'])
예제 #10
0
    def test_daysback_invalid_session_value_default_is_used(self):
        """Daysback session value is invalid: default value is used."""
        PermissionSystem(self.env).grant_permission('user1', 'TIMELINE_VIEW')
        req = MockRequest(self.env, authname='user1', args={'daysback': '--'})
        req.session.set('timeline.daysback', '45')

        data = TimelineModule(self.env).process_request(req)[1]

        self.assertEqual(45, data['daysback'])
예제 #11
0
파일: web_ui.py 프로젝트: hanotch/trac
    def test_no_exception_when_from_year_before_1900(self):
        """Exception not raised when 'from' year before 1900 (#12489)."""
        req = MockRequest(self.env, args={
            'from': '1899-12-23',
            'daysback': 7,
        })

        TimelineModule(self.env).process_request(req)

        self.assertIn('prev', req.chrome['links'])
예제 #12
0
 def __getattr__(self, attrnm):
     """Forward attribute access request to TimelineModule
     """
     try:
         value = getattr(TimelineModule(self.env), attrnm)
         if isinstance(value, MethodType):
             raise AttributeError()
     except AttributeError:
         raise AttributeError("'%s' object has no attribute '%s'" %
                              (self.__class__.__name__, attrnm))
     else:
         return value
예제 #13
0
파일: web_ui.py 프로젝트: hanotch/trac
    def test_invalid_date_format_add_warning(self):
        """Warning is added when date format is invalid."""
        req = MockRequest(self.env, args={
            'from': '2011-02-02T11:38:50 01:00',
        })

        TimelineModule(self.env).process_request(req)

        self.assertIn(u'"2011-02-02T11:38:50 01:00" is an invalid date, '
                      u'or the date format is not known. Try "%s" or "%s" '
                      u'instead.' % (get_date_format_hint(locale_en),
                                     get_date_format_hint('iso8601')),
                      req.chrome['warnings'])
예제 #14
0
    def test_tag_query_save(self):
        """Save timeline tag query string in session."""
        self.assertEqual('tag_query', self.tef.key)

        from trac.timeline.web_ui import TimelineModule
        TimelineModule(self.env)
        perms = PermissionSystem(self.env)
        perms.grant_permission('anonymous', 'TAGS_VIEW')
        perms.grant_permission('anonymous', 'TIMELINE_VIEW')

        req = MockRequest(self.env, path_info='/timeline',
                          args={'tag_query': 'query_str'})
        dispatcher = RequestDispatcher(self.env)
        self.assertRaises(RequestDone, dispatcher.dispatch, req)
        self.assertEqual('query_str', req.session['timeline.tag_query'])
예제 #15
0
    def environment_needs_upgrade(self, db):
        if not self.has_pretty_dateinfo:
            return False

        def find(list, val):
            try:
                return list.index(val)
            except ValueError:
                return len(list)
        filters = RequestDispatcher(self.env).filters
        if find(filters, self) < find(filters, TimelineModule(self.env)):
            return False

        name = self.__class__.__name__
        self.log.warn(
            'Prepend %s to request_filters in [trac] section\n'
            '[trac]\n'
            'request_filters = %s',
            name,
            ','.join([name] + self.config.getlist('trac', 'request_filters')))
        return False
예제 #16
0
 def event_providers(self):
     """Introduce wrappers around timeline event providers in order to
     filter event streams.
     """
     for p in TimelineModule(self.env).event_providers:
         yield TimelineFilterAdapter(p, self.context, self.keep_mismatched)
예제 #17
0
    def expand_macro(self, formatter, name, text, args=None):

        assert text.isdigit(), "Argument must be a number"

        out = "<dl class='lastevents'>"
        add_stylesheet(formatter.req, 'tracprojectmanager/css/lastevents.css')

        all_events = []
        if hasattr(self.env, 'cached_lastevents'):
            expiration = self.env.cached_lastevents[1] + timedelta(
                seconds=EVENT_CACHE_INTERVAL)
            if datetime.now() < expiration:
                all_events = self.env.cached_lastevents[0]

        if not all_events:
            stop = datetime.now(formatter.req.tz)
            start = stop - timedelta(days=EVENT_MAX_DAYS)

            projects = get_project_list(self.env, formatter.req)
            user = formatter.req.authname

            for project, project_path, project_url, env in projects:
                env_timeline = TimelineModule(env)
                for provider in env_timeline.event_providers:
                    filters = [
                        x[0]
                        for x in provider.get_timeline_filters(formatter.req)
                    ]
                    self.env.log.info("Project %s - Filters: %s", project,
                                      filters)
                    try:
                        events = provider.get_timeline_events(
                            formatter.req, start, stop, filters)
                        #self.env.log.info("Event count: %d", len([x for x in events]))

                        for event in events:
                            context = Context(formatter.resource,
                                              Href(project_url),
                                              formatter.req.perm)
                            context.req = formatter.req
                            #context = Context.from_request(formatter.req)

                            if len(event) == 6:  # 0.10 events
                                kind, url, title, date, author, desc = event
                            else:  # 0.11 events
                                if len(event) == 5:  # with special provider
                                    kind, date, author, data, provider = event
                                else:
                                    kind, date, author, data = event

                                title = to_unicode(
                                    provider.render_timeline_event(
                                        context, 'title', event))
                                url = provider.render_timeline_event(
                                    context, 'url', event)
                                desc = to_unicode(
                                    provider.render_timeline_event(
                                        context, 'description', event))

                            all_events.append((project, kind, date, title, url,
                                               author, desc))
                    except Exception, ex:
                        #import sys
                        self.env.log.warning("Exception: %s" %
                                             traceback.format_exc())
                        #out = out + "%s<br/>" % traceback.format_exc()

            all_events.sort(cmp=lambda x, y: x[2] < y[2] and 1 or -1)
            self.env.cached_lastevents = [all_events, datetime.now()]
예제 #18
0
    def _save_attachement(self, req, attachment):
        from trac.web import RequestDone
        from trac.attachment import AttachmentModule, InvalidAttachment
        from trac.resource import get_resource_url
        from trac.timeline.web_ui import TimelineModule
        import os
        import unicodedata
        from trac.util.datefmt import pretty_timedelta

        response = None
        try:
            upload = req.args["attachment"]
            if not hasattr(upload, "filename") or not upload.filename:
                raise TracError(_("No file uploaded"))
            if hasattr(upload.file, "fileno"):
                size = os.fstat(upload.file.fileno())[6]
            else:
                upload.file.seek(0, 2)  # seek to end of file
                size = upload.file.tell()
                upload.file.seek(0)
            if size == 0:
                raise TracError(_("Can't upload empty file"))

            # Maximum attachment size (in bytes)
            max_size = AttachmentModule(self.env).max_size
            if max_size >= 0 and size > max_size:
                raise TracError(_("Maximum attachment size: %(num)s bytes", num=max_size), _("Upload failed"))

            # We try to normalize the filename to unicode NFC if we can.
            # Files uploaded from OS X might be in NFD.
            filename = unicodedata.normalize("NFC", unicode(upload.filename, "utf-8"))
            filename = filename.replace("\\", "/").replace(":", "/")
            filename = os.path.basename(filename)
            if not filename:
                raise TracError(_("No file uploaded"))
            # Now the filename is known, update the attachment resource
            # attachment.filename = filename
            attachment.description = req.args.get("description", "")
            attachment.author = get_reporter_id(req, "author")
            attachment.ipnr = req.remote_addr

            # Validate attachment
            for manipulator in AttachmentModule(self.env).manipulators:
                for field, message in manipulator.validate_attachment(req, attachment):
                    if field:
                        raise InvalidAttachment(
                            _("Attachment field %(field)s is " "invalid: %(message)s", field=field, message=message)
                        )
                    else:
                        raise InvalidAttachment(_("Invalid attachment: %(message)s", message=message))

            if req.args.get("replace"):
                try:
                    old_attachment = Attachment(self.env, attachment.resource(id=filename))
                    if not (old_attachment.author and req.authname and old_attachment.author == req.authname):
                        req.perm(attachment.resource).require("ATTACHMENT_DELETE")
                    if not attachment.description.strip() and old_attachment.description:
                        attachment.description = old_attachment.description
                    old_attachment.delete()
                except TracError:
                    pass  # don't worry if there's nothing to replace
                attachment.filename = None
            attachment.insert(filename, upload.file, size)
            timeline = TimelineModule(self.env).get_timeline_link(
                req, attachment.date, pretty_timedelta(attachment.date), precision="second"
            )
            response = {
                "attachment": {
                    "href": get_resource_url(self.env, attachment.resource, req.href),
                    "realm": attachment.resource.parent.realm,
                    "objid": attachment.resource.parent.id,
                    "filename": filename,
                    "size": size,
                    "author": attachment.author,
                    "description": attachment.description,
                    "timeline": timeline.generate().render().replace("<", "&lt;").replace(">", "&gt;"),
                }
            }
        except (TracError, InvalidAttachment), e:
            response = {"error": e.message}
예제 #19
0
    def _save_attachement(self, req, attachment):
        from trac.web import RequestDone
        from trac.attachment import AttachmentModule, InvalidAttachment
        from trac.resource import get_resource_url
        from trac.timeline.web_ui import TimelineModule
        import os
        import unicodedata
        from trac.util.datefmt import pretty_timedelta

        response = None
        try:
            upload = req.args['attachment']
            if not hasattr(upload, 'filename') or not upload.filename:
                raise TracError(_('No file uploaded'))
            if hasattr(upload.file, 'fileno'):
                size = os.fstat(upload.file.fileno())[6]
            else:
                upload.file.seek(0, 2)  # seek to end of file
                size = upload.file.tell()
                upload.file.seek(0)
            if size == 0:
                raise TracError(_("Can't upload empty file"))

            # Maximum attachment size (in bytes)
            max_size = AttachmentModule(self.env).max_size
            if max_size >= 0 and size > max_size:
                raise TracError(
                    _('Maximum attachment size: %(num)s bytes', num=max_size),
                    _('Upload failed'))

            # We try to normalize the filename to unicode NFC if we can.
            # Files uploaded from OS X might be in NFD.
            filename = unicodedata.normalize('NFC',
                                             unicode(upload.filename, 'utf-8'))
            filename = filename.replace('\\', '/').replace(':', '/')
            filename = os.path.basename(filename)
            if not filename:
                raise TracError(_('No file uploaded'))
            # Now the filename is known, update the attachment resource
            # attachment.filename = filename
            attachment.description = req.args.get('description', '')
            attachment.author = get_reporter_id(req, 'author')
            attachment.ipnr = req.remote_addr

            # Validate attachment
            for manipulator in AttachmentModule(self.env).manipulators:
                for field, message in manipulator.validate_attachment(
                        req, attachment):
                    if field:
                        raise InvalidAttachment(
                            _(
                                'Attachment field %(field)s is '
                                'invalid: %(message)s',
                                field=field,
                                message=message))
                    else:
                        raise InvalidAttachment(
                            _('Invalid attachment: %(message)s',
                              message=message))

            if req.args.get('replace'):
                try:
                    old_attachment = Attachment(
                        self.env, attachment.resource(id=filename))
                    if not (old_attachment.author and req.authname \
                            and old_attachment.author == req.authname):
                        req.perm(
                            attachment.resource).require('ATTACHMENT_DELETE')
                    if (not attachment.description.strip()
                            and old_attachment.description):
                        attachment.description = old_attachment.description
                    old_attachment.delete()
                except TracError:
                    pass  # don't worry if there's nothing to replace
                attachment.filename = None
            attachment.insert(filename, upload.file, size)
            timeline = TimelineModule(self.env).get_timeline_link(
                req,
                attachment.date,
                pretty_timedelta(attachment.date),
                precision='second')
            response = {
                'attachment': {
                    'href':
                    get_resource_url(self.env, attachment.resource, req.href),
                    'realm':
                    attachment.resource.parent.realm,
                    'objid':
                    attachment.resource.parent.id,
                    'filename':
                    filename,
                    'size':
                    size,
                    'author':
                    attachment.author,
                    'description':
                    attachment.description,
                    'timeline':
                    timeline.generate().render().replace('<', '&lt;').replace(
                        '>', '&gt;')
                }
            }
        except (TracError, InvalidAttachment), e:
            response = {'error': e.message}
    def get_timeline_markup(self, req, call, maxrows=10):
        """
        Generates the markup needed when this component is called both 
        explicitly inside wiki pages and implicitly by the ISideBarBoxProvider.

        Note this code uses methods from the trac TimelineModule module.
        """

        chrome = Chrome(self.env)

        # last 14 days should be enough
        stop = datetime.now(req.tz)
        start = stop - timedelta(days=50)

        # use code from trac/timeline to generate event data
        timeline = TimelineModule(self.env)
        available_filters, filters = timeline.get_filters(req)
        include_authors, exclude_authors = timeline.authors()
        events = timeline.get_events(req, start, stop, filters, available_filters, 
                                     include_authors, exclude_authors, maxrows)
        show_gravatar = self.config.get('avatar','mode').lower() != 'off'

        # create the mark up
        context = Context.from_request(req)
        event_list = tag.ul(class_="timeline-list no-style")
        for event in events:
            event_title = event['render']('title', context)
            event_url = event['render']('url', context)
            event_list.append(tag.li(
                show_gravatar and tag.img(
                    src=req.href.avatar(event['author'] if event['author'] else 'anonymous'),
                    class_="avatar",
                ) or "",
                tag.span(
                    chrome.authorinfo(req, event['author']),
                    class_="author"
                ),
                tag.span(
                    pretty_age(event['date']),
                    class_="date",
                ),
                tag.div(
                    tag.i(class_="event-type fa fa-" + event['kind']),
                    tag.a(
                        event_title,
                        href=event_url,
                    ),
                    class_="event-summary"
                ),
                class_="cf"
            ))

        # if the markup is being generated via ISideBarBoxProvider we don't 
        # need to add a span3 class 
        div_classes = "box-sidebar"
        if call == "macro":
            div_classes += " span3 right"
        return tag.div(
                    tag.h3(
                        tag.i(
                            class_="fa fa-calendar"
                        ),
                        " Recent Project Activity"
                    ),
                    event_list,
                    class_=div_classes,
                )
예제 #21
0
 def test_implements_itimelineeventsprovider(self):
     from trac.timeline.web_ui import TimelineModule
     self.assertTrue(self.tep in TimelineModule(self.env).event_providers)