def filter_stream (self, req, method, filename, stream, data):
        if filename not in ('ticket.html', 'query.html'):
            return stream

        if filename == 'ticket.html':
            for xpath in [self._item_to_xpath_t(f) for f in self.single_fields]:
                stream |= Transformer(xpath).attr('class', self.CLASS)

            for xpath in [self._item_to_xpath_t(f) for f in self.multiple_fields]:
                stream |= Transformer(xpath).attr('class', self.CLASS_MULTI)

        if filename == 'query.html':
            for xpath in [self._item_to_xpath_q(f) for f in self.single_fields]:
                stream |= Transformer(xpath).attr('class', self.CLASS)

            for xpath in [self._item_to_xpath_q(f) for f in self.multiple_fields]:
                stream |= Transformer(xpath).attr('class', self.CLASS_MULTI)

        stream |= Transformer('//head').append(tag.style('''
            img.autocompluseraccount { vertical-align: middle; }
            ul.ui-autocomplete li.ui-menu-item span.username {
              font-size: 0.85em;
              font-style: italic;
              color: #888;
            }
            '''))
        return stream
Example #2
0
    def _add_style_attributes(self, req, stream):
        logo = Chrome(self.env).get_logo_data(self.env.abs_href)
        
        if logo:
            height = logo['height']
#            self.log.debug("Logo height: %s" % height)
            if height:
                style_css = '';
                height += 25
                style_css += '#mainnav, #ctxtnav, #pagepath {top: %ipx;}' % height
                height += 50
                style_css += '#content, #notice, #warning, #altlinks, #altlinks, #footer {top: %ipx;}' % height
                style_css = '@media screen { %s }' % style_css
                style_tag = tag.style(style_css, type="text/css")
                stream |= Transformer('.//head').append(style_tag)
            
            # TODO: get logo-src and finding how big it is

            return stream
Example #3
0
 def _parse_params(self):
     self.metatags = [tag.meta(charset=self.document.settings.output_encoding)]
     self.stylesheets = []
     stylesheets = self.document.settings.stylesheet or []
     for href in stylesheets:
         self.stylesheets.append(tag.link(rel='stylesheet', href=href))
     stylesheets_inline = []
     for path in (self.document.settings.stylesheet_inline or []):
         with open(path) as f:
             stylesheets_inline.append(f.read())
     if stylesheets_inline:
         self.stylesheets.append(tag.style(Markup(''.join(stylesheets_inline))))
     self.scripts = []
     scripts = self.document.settings.script or []
     for src, attributes in scripts:
         script = tag.script(src=src)
         if attributes:
             script = script(**{attributes: attributes})
         self.scripts.append(script)
     return
Example #4
0
    def filter_stream(self, req, method, filename, stream, data):

        if filename != "ticket.html" and filename != 'ticket_preview.html':
            return stream

        if 'ticket' in data:
            ticket = data['ticket']

            stream |= Transformer('//head').append(tag.style("""
                .relation_table {
                    width: 100%;
                }
                .relation_table td {
                    border-bottom: dotted 1px #eed;
                }
            """))

            for relation in self.build_relations().values():
                if relation.ticket_type_a == ticket['type']:
                    stream = self._generate_html(relation, relation.relation_type_a, 'a', stream, ticket)
                elif relation.ticket_type_b == ticket['type']:
                    stream = self._generate_html(relation, relation.relation_type_b, 'b', stream, ticket)

        return stream
Example #5
0
    def expand_macro(self, formatter, name, arguments):
        """Returns macro content."""
        env = self.env
        req = formatter.req
        tz = req.tz

        # Parse arguments from macro invocation.
        args, kwargs = parse_args(arguments, strict=False)

        # Enable week number display regardless of argument position.
        week_pref = 'w' in args and args.pop(args.index('w'))

        week_pref = week_pref and week_pref or kwargs.get('w')
        week_start = None
        week_num_start = None
        # Parse per-instance week calculation rules, if available.
        if week_pref:
            if ':' not in week_pref:
                # Treat undelimitted setting as week start.
                week_pref += ':'
            w_start, wn_start = week_pref.split(':')
            try:
                week_start = int(w_start)
            except ValueError:
                week_start = None
            else:
                week_start = week_start > -1 and week_start < 7 and \
                             week_start or None
            try:
                week_num_start = int(wn_start)
            except ValueError:
                week_num_start = None
            else:
                week_num_start = week_num_start in (1, 4, 7) and \
                                 week_num_start or None

        # Respect user's locale, if available.
        try:
            locale = Locale.parse(str(req.locale))
        except (AttributeError, UnknownLocaleError):
            # Attribute 'req.locale' vailable since Trac 0.12.
            locale = None
        if has_babel:
            if locale:
                if not locale.territory:
                    # Search first locale, which has the same `language` and
                    # territory in preferred languages.
                    for l in req.languages:
                        l = l.replace('-', '_').lower()
                        if l.startswith(locale.language.lower() + '_'):
                            try:
                                l = Locale.parse(l)
                                if l.territory:
                                    locale = l
                                    break  # first one rules
                            except UnknownLocaleError:
                                pass
                if not locale.territory and locale.language in LOCALE_ALIASES:
                    locale = Locale.parse(LOCALE_ALIASES[locale.language])
            else:
                # Default fallback.
                locale = Locale('en', 'US')
            env.log.debug('Locale setting for wiki calendar: %s' %
                          locale.get_display_name('en'))
        if not week_start:
            if week_pref and week_pref.lower().startswith('iso'):
                week_start = 0
                week_num_start = 4
            elif has_babel:
                week_start = locale.first_week_day
            else:
                import calendar
                week_start = calendar.firstweekday()
        # ISO calendar will remain as default.
        if not week_num_start:
            if week_start == 6:
                week_num_start = 1
            else:
                week_num_start = 4
        env.log.debug('Effective settings: first_week_day=%s, '
                      '1st_week_of_year_rule=%s' %
                      (week_start, week_num_start))

        # Find year and month of interest.
        year = req.args.get('year')
        # Not clicked on any previous/next button, next look for macro args.
        if not year and len(args) >= 1 and args[0] != "*":
            year = args[0]
        year = year and year.isnumeric() and int(year) or None

        month = req.args.get('month')
        # Not clicked on any previous/next button, next look for macro args.
        if not month and len(args) >= 2 and args[1] != "*":
            month = args[1]
        month = month and month.isnumeric() and int(month) or None

        now = datetime.now(tz)
        # Force offset from start-of-day to avoid a false 'today' marker,
        # but use it only on request of different month/year.
        now.replace(second=1)
        today = None
        if (month and month != now.month) or (year and year != now.year):
            today = now.replace(year=year, month=month, day=1)
        # Use current month and year, if nothing else has been requested.
        if not today:
            today = now.replace(hour=0, minute=0, second=0, microsecond=0)

        showbuttons = True
        if len(args) >= 3 or kwargs.has_key('nav'):
            try:
                showbuttons = kwargs['nav'] in _TRUE_VALUES
            except KeyError:
                showbuttons = args[2] in _TRUE_VALUES

        wiki_page_format = "%Y-%m-%d"
        if len(args) >= 4 and args[3] != "*" or kwargs.has_key('wiki'):
            try:
                wiki_page_format = str(kwargs['wiki'])
            except KeyError:
                wiki_page_format = str(args[3])
        # Support relative paths in macro arguments for wiki page links.
        wiki_page_format = resolve_relative_name(wiki_page_format,
                                                 formatter.resource.id)

        list_condense = 0
        show_t_open_dates = True
        wiki_subpages = []

        # Read optional check plan.
        check = []
        if kwargs.has_key('check'):
            check = kwargs['check'].split('.')

        if name == 'WikiTicketCalendar':
            if len(args) >= 5 or kwargs.has_key('cdate'):
                try:
                    show_t_open_dates = kwargs['cdate'] in _TRUE_VALUES
                except KeyError:
                    show_t_open_dates = args[4] in _TRUE_VALUES

            # TracQuery support for ticket selection
            query_args = "id!=0"
            if len(args) >= 7 or kwargs.has_key('query'):
                # prefer query arguments provided by kwargs
                try:
                    query_args = kwargs['query']
                except KeyError:
                    query_args = args[6]

            # compress long ticket lists
            if len(args) >= 8 or kwargs.has_key('short'):
                # prefer query arguments provided by kwargs
                try:
                    list_condense = int(kwargs['short'])
                except KeyError:
                    list_condense = int(args[7])

            # control calendar display width
            cal_width = "100%;"
            if len(args) >= 9 or kwargs.has_key('width'):
                # prefer query arguments provided by kwargs
                try:
                    cal_width = kwargs['width']
                except KeyError:
                    cal_width = args[8]

            # multiple wiki (sub)pages per day
            if kwargs.has_key('subpages'):
                wiki_subpages = kwargs['subpages'].split('|')

        # Prepare datetime objects for previous/next navigation link creation.
        prev_year = month_offset(today, -12)
        prev_quarter = month_offset(today, -3)
        prev_month = month_offset(today, -1)
        next_month = month_offset(today, 1)
        next_quarter = month_offset(today, 3)
        next_year = month_offset(today, 12)

        # Find first and last calendar day, probably in last/next month,
        # using datetime objects exactly at start-of-day here.
        # Note: Calendar days are numbered 0 (Mo) - 6 (Su).
        first_day_month = today.replace(day=1, second=0)
        first_day = first_day_month - timedelta(
            week_index(first_day_month, week_start))
        last_day_month = next_month.replace(day=1) - timedelta(1)
        if ((last_day_month - first_day).days + 1) % 7 > 0:
            last_day = last_day_month + timedelta(7 - (
                (last_day_month - first_day).days + 1) % 7)
        else:
            last_day = last_day_month

        # Find relevant tickets.
        if name == 'WikiTicketCalendar':
            daystr = (uts and '..' or ':').join([
                format_datetime(first_day, locale=locale),
                format_datetime(last_day, locale=locale)
            ])
            provider = WikiCalendarTicketProvider(env)
            query_args = query_args and query_args + '&' or ''
            tkt_due = provider.harvest(
                req, query_args + '='.join([self.tkt_due_field, daystr]))
            if show_t_open_dates:
                tkt_new = provider.harvest(
                    req, query_args + '='.join(['created', daystr]))

        # Finally building the output now.
        # Begin with caption and optional navigation links.
        buff = tag.tr()

        if showbuttons is True:
            # Create calendar navigation buttons.
            nx = 'next'
            pv = 'prev'
            nav_pv_y = _nav_link(req, '&lt;&lt;', pv, prev_year, locale)
            nav_pv_q = _nav_link(req, '&nbsp;&laquo;', pv, prev_quarter,
                                 locale)
            nav_pv_m = _nav_link(req, '&nbsp;&lt;', pv, prev_month, locale)
            nav_nx_m = _nav_link(req, '&gt;&nbsp;', nx, next_month, locale)
            nav_nx_q = _nav_link(req, '&raquo;&nbsp;', nx, next_quarter,
                                 locale)
            nav_nx_y = _nav_link(req, '&gt;&gt;', nx, next_year, locale)

            # Add buttons for going to previous months and year.
            buff(nav_pv_y, nav_pv_q, nav_pv_m)

        # The caption will always be there.
        if has_babel:
            heading = tag.td(format_datetime(today, 'MMMM y', locale=locale))
        else:
            heading = tag.td(format_date(today, '%B %Y'))
        buff = buff(heading(class_='y'))

        if showbuttons is True:
            # Add buttons for going to next months and year.
            buff(nav_nx_m, nav_nx_q, nav_nx_y)
        buff = tag.caption(tag.table(tag.tbody(buff)))
        buff = tag.table(buff)
        if name == 'WikiTicketCalendar':
            if cal_width.startswith('+') is True:
                width = ":".join(['min-width', cal_width])
                buff(class_='wikitcalendar', style=width)
            else:
                buff(class_='wikitcalendar')
        if name == 'WikiCalendar':
            buff(class_='wiki-calendar')

        heading = tag.tr()
        heading(align='center')
        if week_pref:
            # Add an empty cell matching the week number column below.
            heading(tag.th())

        day_names = [(idx, day_name) for idx, day_name in get_day_names(
            'abbreviated', 'format', locale).iteritems()]
        # Read day names after shifting into correct position.
        for idx, name_ in day_names[week_start:7] + day_names[0:week_start]:
            col = tag.th(name_)
            if has_babel:
                weekend = idx >= locale.weekend_start and \
                          idx <= locale.weekend_end
            else:
                weekend = idx > 4
            col(class_=('workday', 'weekend')[weekend], scope='col')
            heading(col)
        heading = buff(tag.thead(heading))

        # Building main calendar table body
        buff = tag.tbody()
        day = first_day
        while day.date() <= last_day.date():
            # Insert a new row for every week.
            if (day - first_day).days % 7 == 0:
                line = tag.tr()
                line(align='right')
                if week_pref:
                    cell = tag.td(
                        week_num(env, day, week_start, week_num_start))
                    line(cell(class_='week'))
            if not (day < first_day_month or day > last_day_month):
                wiki = format_date(day, wiki_page_format)
                if day == today:
                    a_class = 'day today'
                    td_class = 'today'
                else:
                    a_class = 'day'
                    td_class = 'day'

                if uts:
                    day_ts = to_utimestamp(day)
                    day_ts_eod = day_ts + 86399999999
                else:
                    day_ts = to_timestamp(day)
                    day_ts_eod = day_ts + 86399

                # Check for milestone(s) on that day.
                #db = env.get_read_db()
                #cursor = db.cursor()
                #cursor.execute("""
                #    SELECT name
                #      FROM milestone
                #     WHERE due >= %s and due <= %s
                #""", (day_ts, day_ts_eod))
                cursor = self.env.db_query(
                    """
                    SELECT name
                      FROM milestone
                     WHERE due >= %s and due <= %s
                """, (day_ts, day_ts_eod))
                milestones = tag()
                for row in cursor:
                    if not a_class.endswith('milestone'):
                        a_class += ' milestone'
                    milestone = to_unicode(row[0])
                    url = env.href.milestone(milestone)
                    milestone = '* ' + milestone
                    milestones = tag(
                        milestones,
                        tag.div(tag.a(milestone, href=url),
                                class_='milestone'))
                label = tag.span(day.day)
                label(class_='day')
                # Generate wiki page links with name specified in
                # 'wiki_page_format', and check their existence.
                if len(wiki_subpages) > 0:
                    pages = tag(label, Markup('<br />'))
                    for page in wiki_subpages:
                        label = tag(' ', page[0])
                        page = '/'.join([wiki, page])
                        pages(
                            self._wiki_link(req, args, kwargs, page, label,
                                            'subpage', check))
                else:
                    pages = self._wiki_link(req, args, kwargs, wiki, label,
                                            a_class, check)
                cell = tag.td(pages)
                cell(class_=td_class, valign='top')
                if name == 'WikiCalendar':
                    line(cell)
                else:
                    if milestones:
                        cell(milestones)
                    else:
                        cell(tag.br())

                    match = []
                    match_od = []
                    ticket_heap = tag('')
                    ticket_list = tag.div('')
                    ticket_list(align='left', class_='condense')

                    # Get tickets with due date set to day.
                    for t in tkt_due:
                        due = t.get(self.tkt_due_field)
                        if due is None or due in ('', '--'):
                            continue
                        else:
                            if self.tkt_due_format == 'ts':
                                if not isinstance(due, datetime):
                                    continue
                                if uts:
                                    due_ts = to_utimestamp(due)
                                else:
                                    due_ts = to_timestamp(due)
                                if due_ts < day_ts or due_ts > day_ts_eod:
                                    continue
                            else:
                                # Beware: Format might even be unicode string,
                                # but str is required by the function.
                                duedate = format_date(day,
                                                      str(self.tkt_due_format))
                                if not due == duedate:
                                    continue

                        tkt_id = t.get('id')
                        ticket, short = _ticket_links(env, formatter, t)
                        ticket_heap(ticket)
                        if not tkt_id in match:
                            if len(match) == 0:
                                ticket_list(short)
                            else:
                                ticket_list(', ', short)
                            match.append(tkt_id)

                    # Optionally, get tickets created on day too.
                    if show_t_open_dates:
                        ticket_od_list = tag.div('')
                        ticket_od_list(align='left',
                                       class_='opendate_condense')

                        for t in tkt_new:
                            if uts:
                                ticket_ts = to_utimestamp(t.get('time'))
                            else:
                                ticket_ts = to_timestamp(t.get('time'))
                            if ticket_ts < day_ts or ticket_ts > day_ts_eod:
                                continue

                            a_class = 'opendate_'
                            tkt_id = t.get('id')
                            ticket, short = _ticket_links(
                                env, formatter, t, a_class)
                            ticket_heap(ticket)
                            if not tkt_id in match:
                                if len(match_od) == 0:
                                    ticket_od_list(short)
                                else:
                                    ticket_od_list(', ', short)
                                match_od.append(tkt_id)

                    matches = len(match) + len(match_od)
                    if list_condense > 0 and matches >= list_condense:
                        if len(match_od) > 0:
                            if len(match) > 0:
                                ticket_list(', ')
                            ticket_list = tag(ticket_list, ticket_od_list)
                        line(cell(ticket_list))
                    else:
                        line(cell(ticket_heap))
            else:
                if name == 'WikiCalendar':
                    wiki = format_date(day, wiki_page_format)
                    a_class = 'day adjacent_month'
                    pages = self._wiki_link(req, args, kwargs, wiki, day.day,
                                            a_class)
                    cell = tag.td(pages, class_='day adjacent_month')
                    line(cell)
                else:
                    cell = tag.td('', class_='day adjacent_month')
                    line(cell)
            # Append completed week rows.
            if (day - first_day).days % 7 == 6:
                buff(line)
            day += timedelta(1)

        buff = tag.div(heading(buff))
        if name == 'WikiTicketCalendar':
            if cal_width.startswith('+') is True:
                width = ":".join(['width', cal_width])
                buff(class_='wikitcalendar', style=width)
            else:
                buff(class_='wikitcalendar')
        if name == 'WikiCalendar':
            buff(class_='wiki-calendar')
        # Add common CSS stylesheet.
        if self.internal_css and not req.args.get('wikicalendar'):
            # Put definitions directly into the output.
            f = open('/'.join([self.htdocs_path, 'wikicalendar.css']), 'Ur')
            css = tag.style(Markup('<!--\n'), '\n'.join(f.readlines()),
                            Markup('-->\n'))(type="text/css")
            f.close()
            # Add hint to prevent multiple inclusions.
            req.args['wikicalendar'] = True
            return tag(css, buff)
        elif not req.args.get('wikicalendar'):
            add_stylesheet(req, 'wikicalendar/wikicalendar.css')
        return buff
Example #6
0
    def expand_macro(self, formatter, name, arguments):

        self.ref = formatter
        self.tz_info = formatter.req.tz
        self.thistime = datetime.datetime.now(self.tz_info)

        # Parse arguments from macro invocation
        args, kwargs = parse_args(arguments, strict=False)

        # Find out whether use http param, current or macro param year/month
        http_param_year = formatter.req.args.get('year','')
        http_param_month = formatter.req.args.get('month','')

        if http_param_year == "":
            # not clicked on a prev or next button
            if len(args) >= 1 and args[0] <> "*":
                # year given in macro parameters
                year = int(args[0])
            else:
                # use current year
                year = self.thistime.year
        else:
            # year in http params (clicked by user) overrides everything
            year = int(http_param_year)

        if http_param_month == "":
            # not clicked on a prev or next button
            if len(args) >= 2 and args[1] <> "*":
                # month given in macro parameters
                month = int(args[1])
            else:
                # use current month
                month = self.thistime.month
        else:
            # month in http params (clicked by user) overrides everything
            month = int(http_param_month)

        showbuttons = True
        if len(args) >= 3 or kwargs.has_key('nav'):
            try:
                showbuttons = kwargs['nav'] in ["True", "true", "yes", "1"]
            except KeyError:
                showbuttons = args[2] in ["True", "true", "yes", "1"]

        wiki_page_format = "%Y-%m-%d"
        if len(args) >= 4 and args[3] != "*" or kwargs.has_key('wiki'):
            try:
                wiki_page_format = str(kwargs['wiki'])
            except KeyError:
                wiki_page_format = str(args[3])
        # Support relative paths in macro arguments for wiki page links.
        wiki_page_format = self._resolve_relative_name(wiki_page_format,
                                                       formatter.resource.id)

        list_condense = 0
        show_t_open_dates = True
        wiki_page_template = ""
        wiki_subpages = []

        # Read optional check plan.
        check = []
        if kwargs.has_key('check'):
            check = kwargs['check'].split('.')

        if name == 'WikiTicketCalendar':
            if len(args) >= 5 or kwargs.has_key('cdate'):
                try:
                    show_t_open_dates = kwargs['cdate'] in \
                                               ["True", "true", "yes", "1"]
                except KeyError:
                    show_t_open_dates = args[4] in \
                                               ["True", "true", "yes", "1"]

        # Optional page template to create new wiki pages.
        # The default (empty page) is used, if the template name is invalid.
        if len(args) >= 6 or kwargs.has_key('base'):
            try:
                wiki_page_template = kwargs['base']
            except KeyError:
                wiki_page_template = args[5]

        if name == 'WikiTicketCalendar':
            # TracQuery support for ticket selection
            query_args = "id!=0"
            if len(args) >= 7 or kwargs.has_key('query'):
                # prefer query arguments provided by kwargs
                try:
                    query_args = kwargs['query']
                except KeyError:
                    query_args = args[6]
            tickets = WikiCalendarTicketProvider(self.env)
            self.tickets = tickets.harvest(formatter.req, query_args)

            # compress long ticket lists
            if len(args) >= 8 or kwargs.has_key('short'):
                # prefer query arguments provided by kwargs
                try:
                    list_condense = int(kwargs['short'])
                except KeyError:
                    list_condense = int(args[7])

            # control calendar display width
            cal_width = "100%;"
            if len(args) >= 9 or kwargs.has_key('width'):
                # prefer query arguments provided by kwargs
                try:
                    cal_width = kwargs['width']
                except KeyError:
                    cal_width = args[8]

            # multiple wiki (sub)pages per day
            if kwargs.has_key('subpages'):
                wiki_subpages = kwargs['subpages'].split('|')

            # Can use this to change the day the week starts on,
            # but this is a system-wide setting.
            calendar.setfirstweekday(calendar.MONDAY)

        cal = calendar.monthcalendar(year, month)
        curr_day = None
        if year == self.thistime.year and month == self.thistime.month:
            curr_day = self.thistime.day

        # for prev/next navigation links
        prevMonth = month - 1
        nextMonth = month + 1
        nextYear = prevYear = year
        # check for year change (KISS version)
        if prevMonth == 0:
            prevMonth = 12
            prevYear -= 1
        if nextMonth == 13:
            nextMonth = 1
            nextYear += 1

        # for fast-forward/-rewind navigation links
        ffYear = frYear = year
        if month < 4:
            frMonth = month + 9
            frYear -= 1
        else:
            frMonth = month - 3
        if month > 9:
            ffMonth = month - 9
            ffYear += 1
        else:
            ffMonth = month + 3

        last_week_prevMonth = calendar.monthcalendar(prevYear, prevMonth)[-1]
        first_week_nextMonth = calendar.monthcalendar(nextYear, nextMonth)[0]

        # Switch to user's locale, if available.
        try:
            loc_req = str(formatter.req.locale)
        except AttributeError:
            # Available since in Trac 0.12 .
            loc_req = None
        if loc_req:
            loc = locale.getlocale()
            loc_prop = locale.normalize(loc_req)
            try:
                locale.setlocale(locale.LC_TIME, loc_prop)
            except locale.Error:
                try:
                    # Re-try with UTF-8 as last resort.
                    loc_prop = '.'.join([loc_prop.split('.')[0],'utf8'])
                    locale.setlocale(locale.LC_TIME, loc_prop)
                except locale.Error:
                    loc_prop = None
            self.env.log.debug('Locale setting for calendar: ' + str(loc_prop))

        # Finally building the output
        # Begin with caption and optional navigation links
        buff = tag.tr()

        if showbuttons is True:
            # calendar navigation buttons
            nx = 'next'
            pv = 'prev'
            nav_pvY = self._mknav('&lt;&lt;', pv, month, year-1)
            nav_frM = self._mknav('&nbsp;&lt;', pv, frMonth, frYear)
            nav_pvM = self._mknav('&nbsp;&laquo;', pv, prevMonth, prevYear)
            nav_nxM = self._mknav('&raquo;&nbsp;', nx, nextMonth, nextYear)
            nav_ffM = self._mknav('&gt;&nbsp;', nx, ffMonth, ffYear)
            nav_nxY = self._mknav('&gt;&gt;', nx, month, year+1)

            # add buttons for going to previous months and year
            buff(nav_pvY, nav_frM, nav_pvM)

        # The caption will always be there.
        heading = tag.td(
             to_unicode(format_date(self._mkdatetime(year, month), '%B %Y')))
        buff = buff(heading(class_='y'))

        if showbuttons is True:
            # add buttons for going to next months and year
            buff(nav_nxM, nav_ffM, nav_nxY)
        buff = tag.caption(tag.table(tag.tbody(buff)))
        buff = tag.table(buff)
        if name == 'WikiTicketCalendar':
            if cal_width.startswith('+') is True:
                width=":".join(['min-width', cal_width]) 
                buff(class_='wikitcalendar', style=width)
            else:
                buff(class_='wikitcalendar')
        if name == 'WikiCalendar':
                buff(class_='wiki-calendar')

        heading = tag.tr()
        heading(align='center')

        for day in calendar.weekheader(2).split()[:-2]:
            col = tag.th(to_unicode(day))
            col(class_='workday', scope='col')
            heading(col)
        for day in calendar.weekheader(2).split()[-2:]:
            col = tag.th(to_unicode(day))
            col(class_='weekend', scope='col')
            heading(col)

        heading = buff(tag.thead(heading))

        # Building main calendar table body
        buff = tag.tbody()
        w = -1
        for week in cal:
            w = w + 1
            line = tag.tr()
            line(align='right')
            d = -1
            for day in week:
                d = d + 1
                if day:
                    # check for wikipage with name specified in
                    # 'wiki_page_format'
                    wiki = format_date(self._mkdatetime(year, month, day),
                                                         wiki_page_format)
                    if day == curr_day:
                        a_class = 'day today'
                        td_class = 'today'
                    else:
                        a_class = 'day'
                        td_class = 'day'

                    day_dt = self._mkdatetime(year, month, day)
                    if uts:
                        day_ts = to_utimestamp(day_dt)
                        day_ts_eod = day_ts + 86399999999
                    else:
                        day_ts = to_timestamp(day_dt)
                        day_ts_eod = day_ts + 86399

                    # check for milestone(s) on that day
                    db = self.env.get_db_cnx()
                    cursor = db.cursor()
                    cursor.execute("""
                        SELECT name
                          FROM milestone
                         WHERE due >= %s and due <= %s
                    """, (day_ts, day_ts_eod))
                    milestones = tag()
                    for row in cursor:
                        if not a_class.endswith('milestone'):
                            a_class += ' milestone'
                        milestone = to_unicode(row[0])
                        url = self.env.href.milestone(milestone)
                        milestone = '* ' + milestone
                        milestones = tag(milestones,
                                         tag.div(tag.a(milestone, href=url),
                                                 class_='milestone'))
                    day = tag.span(day)
                    day(class_='day')
                    if len(wiki_subpages) > 0:
                        pages = tag(day, Markup('<br />'))
                        for page in wiki_subpages:
                            label = tag(' ', page[0])
                            page = '/'.join([wiki, page])
                            url = self.env.href.wiki(page)
                            pages(self._gen_wiki_links(page, label, 'subpage',
                                                     url, wiki_page_template,
                                                     check))
                    else:
                        url = self.env.href.wiki(wiki)
                        pages = self._gen_wiki_links(wiki, day, a_class,
                                                     url, wiki_page_template,
                                                     check)
                    cell = tag.td(pages)
                    cell(class_=td_class, valign='top')
                    if name == 'WikiCalendar':
                        line(cell)
                    else:
                        if milestones:
                            cell(milestones)
                        else:
                            cell(tag.br())

                        match = []
                        match_od = []
                        ticket_heap = tag('')
                        ticket_list = tag.div('')
                        ticket_list(align='left', class_='condense')

                        # get tickets with due date set to day
                        for t in self.tickets:
                            due = t.get(self.tkt_due_field)
                            if due is None or due in ['', '--']:
                                continue
                            else:
                                if self.tkt_due_format == 'ts':
                                    if not isinstance(due, datetime.datetime):
                                        continue
                                    if uts:
                                        due_ts = to_utimestamp(due)
                                    else:
                                        due_ts = to_timestamp(due)
                                    if due_ts < day_ts or due_ts > day_ts_eod:
                                        continue
                                else:
                                    # Beware: Format might even be unicode str
                                    duedate = format_date(day_dt,
                                                      str(self.tkt_due_format))
                                    if not due == duedate:
                                        continue

                            id = t.get('id')
                            ticket, short = self._gen_ticket_entry(t)
                            ticket_heap(ticket)
                            if not id in match:
                                if len(match) == 0:
                                    ticket_list(short)
                                else:
                                    ticket_list(', ', short)
                                match.append(id)

                        # optionally get tickets created on day
                        if show_t_open_dates is True:
                            ticket_od_list = tag.div('')
                            ticket_od_list(align='left',
                                           class_='opendate_condense')

                            for t in self.tickets:
                                if uts:
                                    ticket_ts = to_utimestamp(t.get('time'))
                                else:
                                    ticket_ts = to_timestamp(t.get('time'))
                                if ticket_ts < day_ts or \
                                        ticket_ts > day_ts_eod:
                                    continue

                                a_class = 'opendate_'
                                id = t.get('id')
                                ticket, short = self._gen_ticket_entry(t,
                                                                  a_class)
                                ticket_heap(ticket)
                                if not id in match:
                                    if len(match_od) == 0:
                                        ticket_od_list(short)
                                    else:
                                        ticket_od_list(', ', short)
                                    match_od.append(id)

                        matches = len(match) + len(match_od)
                        if list_condense > 0 and matches >= list_condense:
                            if len(match_od) > 0:
                                if len(match) > 0:
                                    ticket_list(', ')
                                ticket_list = tag(ticket_list, ticket_od_list)
                            line(cell(ticket_list))
                        else:
                            line(cell(ticket_heap))
                else:
                    if name == 'WikiCalendar':
                        if w == 0:
                            day = last_week_prevMonth[d]
                            wiki = format_date(self._mkdatetime(
                                prevYear, prevMonth, day), wiki_page_format)
                        else:
                            day = first_week_nextMonth[d]
                            wiki = format_date(self._mkdatetime(
                                nextYear, nextMonth, day), wiki_page_format)
                        url = self.env.href.wiki(wiki)
                        a_class = 'day adjacent_month'
                        pages = self._gen_wiki_links(wiki, day, a_class,
                                                 url, wiki_page_template)

                        cell = tag.td(pages)
                        cell(class_='day adjacent_month')
                        line(cell)
                    else:
                        cell = tag.td('')
                        cell(class_='day adjacent_month')
                        line(cell)
            buff(line)

        if loc_req and loc_prop:
            # We may have switched to users locale, resetting now.
            try:
                locale.setlocale(locale.LC_ALL, loc)
                self.env.log.debug('Locale setting restored: ' + str(loc))
            except locale.Error:
                pass

        buff = tag.div(heading(buff))
        if name == 'WikiTicketCalendar':
            if cal_width.startswith('+') is True:
                width=":".join(['width', cal_width]) 
                buff(class_='wikitcalendar', style=width)
            else:
                buff(class_='wikitcalendar')
        if name == 'WikiCalendar':
                buff(class_='wiki-calendar')
        # Add common CSS stylesheet
        if self.internal_css and not self.ref.req.args.get('wikicalendar'):
            # Put definitions directly into the output.
            f = open('/'.join([self.htdocs_path, 'wikicalendar.css']), 'Ur')
            css = tag.style(Markup('<!--\n'), '\n'.join(f.readlines()),
                            Markup('-->\n'))(type="text/css")
            f.close()
            # Add hint to prevent multiple inclusions.
            self.ref.req.args['wikicalendar'] = True
            return tag(css, buff)
        elif not self.ref.req.args.get('wikicalendar'):
            add_stylesheet(self.ref.req, 'wikicalendar/wikicalendar.css')
        return buff
    def expand_macro(self, formatter, name, arguments):

        self.ref = formatter
        self.tz_info = formatter.req.tz
        self.thistime = datetime.datetime.now(self.tz_info)

        # Parse arguments from macro invocation
        args, kwargs = parse_args(arguments, strict=False)

        # Find out whether use http param, current or macro param year/month
        http_param_year = formatter.req.args.get('year', '')
        http_param_month = formatter.req.args.get('month', '')

        if http_param_year == "":
            # not clicked on a prev or next button
            if len(args) >= 1 and args[0] <> "*":
                # year given in macro parameters
                year = int(args[0])
            else:
                # use current year
                year = self.thistime.year
        else:
            # year in http params (clicked by user) overrides everything
            year = int(http_param_year)

        if http_param_month == "":
            # not clicked on a prev or next button
            if len(args) >= 2 and args[1] <> "*":
                # month given in macro parameters
                month = int(args[1])
            else:
                # use current month
                month = self.thistime.month
        else:
            # month in http params (clicked by user) overrides everything
            month = int(http_param_month)

        showbuttons = True
        if len(args) >= 3 or kwargs.has_key('nav'):
            try:
                showbuttons = kwargs['nav'] in ["True", "true", "yes", "1"]
            except KeyError:
                showbuttons = args[2] in ["True", "true", "yes", "1"]

        wiki_page_format = "%Y-%m-%d"
        if len(args) >= 4 and args[3] != "*" or kwargs.has_key('wiki'):
            try:
                wiki_page_format = str(kwargs['wiki'])
            except KeyError:
                wiki_page_format = str(args[3])

        show_t_open_dates = True
        if len(args) >= 5 or kwargs.has_key('cdate'):
            try:
                show_t_open_dates = kwargs['cdate'] in \
                                               ["True", "true", "yes", "1"]
            except KeyError:
                show_t_open_dates = args[4] in ["True", "true", "yes", "1"]

        # template name tried to create new pages
        # optional, default (empty page) is used, if name is invalid
        wiki_page_template = ""
        if len(args) >= 6 or kwargs.has_key('base'):
            try:
                wiki_page_template = kwargs['base']
            except KeyError:
                wiki_page_template = args[5]

        # TracQuery support for ticket selection
        query_args = "id!=0"
        if len(args) >= 7 or kwargs.has_key('query'):
            # prefer query arguments provided by kwargs
            try:
                query_args = kwargs['query']
            except KeyError:
                query_args = args[6]
        self.tickets = self._ticket_query(formatter, query_args)

        # compress long ticket lists
        list_condense = 0
        if len(args) >= 8 or kwargs.has_key('short'):
            # prefer query arguments provided by kwargs
            try:
                list_condense = int(kwargs['short'])
            except KeyError:
                list_condense = int(args[7])

        # control calendar display width
        cal_width = "100%;"
        if len(args) >= 9 or kwargs.has_key('width'):
            # prefer query arguments provided by kwargs
            try:
                cal_width = kwargs['width']
            except KeyError:
                cal_width = args[8]

        # Can use this to change the day the week starts on,
        # but this is a system-wide setting.
        calendar.setfirstweekday(calendar.MONDAY)
        cal = calendar.monthcalendar(year, month)

        curr_day = None
        if year == self.thistime.year and month == self.thistime.month:
            curr_day = self.thistime.day

        # Compile regex pattern before use for better performance
        pattern_del = '(?:<span .*?>)|(?:</span>)'
        pattern_del += '|(?:<p>)|(?:<p .*?>)|(?:</p>)'
        pattern_del += '|(?:</table>)|(?:<td.*?\n)|(?:<tr.*?</tr>)'
        self.end_RE = re.compile('(?:</a>)')
        self.del_RE = re.compile(pattern_del)
        self.item_RE = re.compile('(?:<img .*?>)')
        self.open_RE = re.compile('(?:<a .*?>)')
        self.tab_RE = re.compile('(?:<table .*?>)')

        # for prev/next navigation links
        prevMonth = month - 1
        nextMonth = month + 1
        nextYear = prevYear = year
        # check for year change (KISS version)
        if prevMonth == 0:
            prevMonth = 12
            prevYear -= 1
        if nextMonth == 13:
            nextMonth = 1
            nextYear += 1

        # for fast-forward/-rewind navigation links
        ffYear = frYear = year
        if month < 4:
            frMonth = month + 9
            frYear -= 1
        else:
            frMonth = month - 3
        if month > 9:
            ffMonth = month - 9
            ffYear += 1
        else:
            ffMonth = month + 3

        # Finally building the output
        # Prepending inline CSS definitions
        styles = """ \
<!--
table.wikiTicketCalendar th { font-weight: bold; }
table.wikiTicketCalendar th.workday { width: 17%; }
table.wikiTicketCalendar th.weekend { width: 7%; }
table.wikiTicketCalendar caption {
    font-size: 120%; white-space: nowrap;
    }
table.wikiTicketCalendar caption a {
    display: inline; margin: 0; border: 0; padding: 0;
    background-color: transparent; color: #b00; text-decoration: none;
    }
table.wikiTicketCalendar caption a.prev { padding-right: 5px; font: bold; }
table.wikiTicketCalendar caption a.next { padding-left: 5px; font: bold; }
table.wikiTicketCalendar caption a:hover { background-color: #eee; }
table.wikiTicketCalendar td.today {
    background: #fbfbfb; border-color: #444444; color: #444;
    border-style: solid; border-width: 1px;
    }
table.wikiTicketCalendar td.day {
    background: #e5e5e5; border-color: #444444; color: #333;
    border-style: solid; border-width: 1px;
    }
table.wikiTicketCalendar td.fill {
    background: #eee; border-color: #ccc;
    border-style: solid; border-width: 1px;
    }
table.wikiTicketCalendar div.milestone {
    font-size: 9px; background: #f7f7f0; border: 1px solid #d7d7d7;
    border-bottom-color: #999; text-align: left;
    }
table.wikiTicketCalendar a.day {
    width: 2em; height: 100%; margin: 0; border: 0px; padding: 0;
    color: #333; text-decoration: none;
    }
table.wikiTicketCalendar a.day_haspage {
    width: 2em; height: 100%; margin: 0; border: 0px; padding: 0;
    color: #b00 !important; text-decoration: none;
    }
table.wikiTicketCalendar a.day:hover {
    border-color: #eee; background-color: #eee; color: #000;
    }
table.wikiTicketCalendar div.open, span.open   {
    font-size: 9px; color: #000000;
    }
table.wikiTicketCalendar div.closed, span.closed {
    font-size: 9px; color: #777777; text-decoration: line-through;
    }
table.wikiTicketCalendar div.opendate_open, span.opendate_open {
    font-size: 9px; color: #000077;
    }
table.wikiTicketCalendar div.opendate_closed, span.opendate_closed {
    font-size: 9px; color: #000077; text-decoration: line-through;
    }
table.wikiTicketCalendar div.condense { background-color: #e5e5e5; }
table.wikiTicketCalendar div.opendate_condense { background-color: #cdcdfa; }

/* pure CSS style tooltip */
a.tip {
    position: relative; cursor: help;
    }
a.tip span { display: none; }
a.tip:hover span {
    display: block; z-index: 1;
    font-size: 0.75em; text-decoration: none;
    /* big left move because of z-index render bug in IE<8 */
    position: absolute; top: 0.8em; left: 6em;
    border: 1px solid #000; background-color: #fff; padding: 5px;
    }
-->
"""

        # create inline style definition as Genshi fragment
        styles = tag.style(Markup(styles))
        styles(type='text/css')

        # Build caption and optional navigation links
        buff = tag.caption()

        if showbuttons is True:
            # calendar navigation buttons
            nx = 'next'
            pv = 'prev'
            nav_pvY = self._mknav('&nbsp;&lt;&lt;', pv, month, year - 1)
            nav_frM = self._mknav('&nbsp;&lt;&nbsp;', pv, frMonth, frYear)
            nav_pvM = self._mknav('&nbsp;&laquo;&nbsp;', pv, prevMonth,
                                  prevYear)
            nav_nxM = self._mknav('&nbsp;&raquo;&nbsp;', nx, nextMonth,
                                  nextYear)
            nav_ffM = self._mknav('&nbsp;&gt;&nbsp;', nx, ffMonth, ffYear)
            nav_nxY = self._mknav('&nbsp;&gt;&gt;', nx, month, year + 1)

            # add buttons for going to previous months and year
            buff(nav_pvY, nav_frM, nav_pvM)

        # The caption will always be there.
        buff(
            tag.strong(
                to_unicode(format_date(self._mkdatetime(year, month),
                                       '%B %Y'))))

        if showbuttons is True:
            # add buttons for going to next months and year
            buff(nav_nxM, nav_ffM, nav_nxY)

        buff = tag.table(buff)
        width = ":".join(['min-width', cal_width])
        buff(class_='wikiTicketCalendar', style=width)

        heading = tag.tr()
        heading(align='center')

        for day in calendar.weekheader(2).split()[:-2]:
            col = tag.th(day)
            col(class_='workday', scope='col')
            heading(col)
        for day in calendar.weekheader(2).split()[-2:]:
            col = tag.th(day)
            col(class_='weekend', scope='col')
            heading(col)

        heading = buff(tag.thead(heading))

        # Building main calendar table body
        buff = tag.tbody()
        for row in cal:
            line = tag.tr()
            line(align='right')
            for day in row:
                if not day:
                    cell = tag.td('')
                    cell(class_='fill')
                    line(cell)
                else:
                    # check for wikipage with name specified in
                    # 'wiki_page_format'
                    wiki = format_date(self._mkdatetime(year, month, day),
                                       wiki_page_format)
                    url = self.env.href.wiki(wiki)
                    if WikiSystem(self.env).has_page(wiki):
                        a_class = "day_haspage"
                        title = "Go to page %s" % wiki
                    else:
                        a_class = "day"
                        url += "?action=edit"
                        # adding template name, if specified
                        if wiki_page_template != "":
                            url += "&template=" + wiki_page_template
                        title = "Create page %s" % wiki
                    if day == curr_day:
                        td_class = 'today'
                    else:
                        td_class = 'day'

                    cell = tag.a(tag.b(day), href=url)
                    cell(class_=a_class, title_=title)
                    cell = tag.td(cell)
                    cell(class_=td_class, valign='top')

                    day_dt = self._mkdatetime(year, month, day)
                    if uts:
                        day_ts = to_utimestamp(day_dt)
                        day_ts_eod = day_ts + 86399999999
                    else:
                        day_ts = to_timestamp(day_dt)
                        day_ts_eod = day_ts + 86399

                    # check for milestone(s) on that day
                    db = self.env.get_db_cnx()
                    cursor = db.cursor()
                    cursor.execute(
                        """
                        SELECT name
                          FROM milestone
                         WHERE due >= %s and due <= %s
                    """, (day_ts, day_ts_eod))
                    while (1):
                        row = cursor.fetchone()
                        if row is None:
                            cell(tag.br())
                            break
                        else:
                            name = to_unicode(row[0])
                            url = self.env.href.milestone(name)
                            milestone = '* ' + name
                            milestone = tag.div(tag.a(milestone, href=url))
                            milestone(class_='milestone')

                            cell(milestone)

                    match = []
                    match_od = []
                    ticket_heap = tag('')
                    ticket_list = tag.div('')
                    ticket_list(align='left', class_='condense')

                    # get tickets with due date set to day
                    for t in self.tickets:
                        due = t.get(self.due_field_name)
                        if due is None or due in ['', '--']:
                            continue
                        else:
                            if self.due_field_fmt == 'ts':
                                if not isinstance(due, datetime.datetime):
                                    continue
                                else:
                                    if uts:
                                        due_ts = to_utimestamp(due)
                                    else:
                                        due_ts = to_timestamp(due)
                                if due_ts < day_ts or \
                                    due_ts > day_ts_eod:
                                    continue
                            else:
                                duedate = format_date(day_dt,
                                                      self.due_field_fmt)
                                if not due == duedate:
                                    continue

                        id = t.get('id')
                        ticket, short = self._gen_ticket_entry(t)
                        ticket_heap(ticket)
                        if not id in match:
                            if len(match) == 0:
                                ticket_list(short)
                            else:
                                ticket_list(', ', short)
                            match.append(id)

                    # optionally get tickets created on day
                    if show_t_open_dates is True:
                        ticket_od_list = tag.div('')
                        ticket_od_list(align='left',
                                       class_='opendate_condense')

                        for t in self.tickets:
                            if uts:
                                ticket_ts = to_utimestamp(t.get('time'))
                            else:
                                ticket_ts = to_timestamp(t.get('time'))
                            if ticket_ts < day_ts or ticket_ts > day_ts_eod:
                                continue

                            a_class = 'opendate_'
                            id = t.get('id')
                            ticket, short = self._gen_ticket_entry(t, a_class)
                            ticket_heap(ticket)
                            if not id in match:
                                if len(match_od) == 0:
                                    ticket_od_list(short)
                                else:
                                    ticket_od_list(', ', short)
                                match_od.append(id)

                    matches = len(match) + len(match_od)
                    if list_condense > 0 and matches >= list_condense:
                        if len(match_od) > 0:
                            if len(match) > 0:
                                ticket_list(', ')
                            ticket_list = tag(ticket_list, ticket_od_list)
                        line(cell(ticket_list))
                    else:
                        line(cell(ticket_heap))

            buff(line)

        buff = tag.div(heading(buff))
        if cal_width.startswith('+') is True:
            width = ":".join(['width', cal_width])
            buff(class_='wikiTicketCalendar', style=width)
        else:
            buff(class_='wikiTicketCalendar')

        # Finally prepend prepared CSS styles
        buff = tag(styles, buff)

        return buff
    def expand_macro(self, formatter, name, arguments):

        self.ref = formatter
        self.tz_info = formatter.req.tz
        self.thistime = datetime.datetime.now(self.tz_info)

        # Parse arguments from macro invocation
        args, kwargs = parse_args(arguments, strict=False)

        # Find out whether use http param, current or macro param year/month
        http_param_year = formatter.req.args.get('year','')
        http_param_month = formatter.req.args.get('month','')

        if http_param_year == "":
            # not clicked on a prev or next button
            if len(args) >= 1 and args[0] <> "*":
                # year given in macro parameters
                year = int(args[0])
            else:
                # use current year
                year = self.thistime.year
        else:
            # year in http params (clicked by user) overrides everything
            year = int(http_param_year)

        if http_param_month == "":
            # not clicked on a prev or next button
            if len(args) >= 2 and args[1] <> "*":
                # month given in macro parameters
                month = int(args[1])
            else:
                # use current month
                month = self.thistime.month
        else:
            # month in http params (clicked by user) overrides everything
            month = int(http_param_month)

        showbuttons = True
        if len(args) >= 3 or kwargs.has_key('nav'):
            try:
                showbuttons = kwargs['nav'] in ["True", "true", "yes", "1"]
            except KeyError:
                showbuttons = args[2] in ["True", "true", "yes", "1"]

        wiki_page_format = "%Y-%m-%d"
        if len(args) >= 4 and args[3] != "*" or kwargs.has_key('wiki'):
            try:
                wiki_page_format = str(kwargs['wiki'])
            except KeyError:
                wiki_page_format = str(args[3])

        show_t_open_dates = True
        if len(args) >= 5 or kwargs.has_key('cdate'):
            try:
                show_t_open_dates = kwargs['cdate'] in \
                                               ["True", "true", "yes", "1"]
            except KeyError:
                show_t_open_dates = args[4] in ["True", "true", "yes", "1"]

        # template name tried to create new pages
        # optional, default (empty page) is used, if name is invalid
        wiki_page_template = ""
        if len(args) >= 6 or kwargs.has_key('base'):
            try:
                wiki_page_template = kwargs['base']
            except KeyError:
                wiki_page_template = args[5]

        # TracQuery support for ticket selection
        query_args = "id!=0"
        if len(args) >= 7 or kwargs.has_key('query'):
            # prefer query arguments provided by kwargs
            try:
                query_args = kwargs['query']
            except KeyError:
                query_args = args[6]
        self.tickets = self._ticket_query(formatter, query_args)

        # compress long ticket lists
        list_condense = 0
        if len(args) >= 8 or kwargs.has_key('short'):
            # prefer query arguments provided by kwargs
            try:
                list_condense = int(kwargs['short'])
            except KeyError:
                list_condense = int(args[7])

        # control calendar display width
        cal_width = "100%;"
        if len(args) >= 9 or kwargs.has_key('width'):
            # prefer query arguments provided by kwargs
            try:
                cal_width = kwargs['width']
            except KeyError:
                cal_width = args[8]


        # Can use this to change the day the week starts on,
        # but this is a system-wide setting.
        calendar.setfirstweekday(calendar.MONDAY)
        cal = calendar.monthcalendar(year, month)

        curr_day = None
        if year == self.thistime.year and month == self.thistime.month:
            curr_day = self.thistime.day

        # Compile regex pattern before use for better performance
        pattern_del  = '(?:<span .*?>)|(?:</span>)'
        pattern_del += '|(?:<p>)|(?:<p .*?>)|(?:</p>)'
        pattern_del += '|(?:</table>)|(?:<td.*?\n)|(?:<tr.*?</tr>)'
        self.end_RE  = re.compile('(?:</a>)')
        self.del_RE  = re.compile(pattern_del)
        self.item_RE = re.compile('(?:<img .*?>)')
        self.open_RE = re.compile('(?:<a .*?>)')
        self.tab_RE  = re.compile('(?:<table .*?>)')

        # for prev/next navigation links
        prevMonth = month - 1
        nextMonth = month + 1
        nextYear = prevYear = year
        # check for year change (KISS version)
        if prevMonth == 0:
            prevMonth = 12
            prevYear -= 1
        if nextMonth == 13:
            nextMonth = 1
            nextYear += 1

        # for fast-forward/-rewind navigation links
        ffYear = frYear = year
        if month < 4:
            frMonth = month + 9
            frYear -= 1
        else:
            frMonth = month - 3
        if month > 9:
            ffMonth = month - 9
            ffYear += 1
        else:
            ffMonth = month + 3


        # Finally building the output
        # Prepending inline CSS definitions
        styles = """ \
<!--
table.wikiTicketCalendar th { font-weight: bold; }
table.wikiTicketCalendar th.workday { width: 17%; }
table.wikiTicketCalendar th.weekend { width: 7%; }
table.wikiTicketCalendar caption {
    font-size: 120%; white-space: nowrap;
    }
table.wikiTicketCalendar caption a {
    display: inline; margin: 0; border: 0; padding: 0;
    background-color: transparent; color: #b00; text-decoration: none;
    }
table.wikiTicketCalendar caption a.prev { padding-right: 5px; font: bold; }
table.wikiTicketCalendar caption a.next { padding-left: 5px; font: bold; }
table.wikiTicketCalendar caption a:hover { background-color: #eee; }
table.wikiTicketCalendar td.today {
    background: #fbfbfb; border-color: #444444; color: #444;
    border-style: solid; border-width: 1px;
    }
table.wikiTicketCalendar td.day {
    background: #e5e5e5; border-color: #444444; color: #333;
    border-style: solid; border-width: 1px;
    }
table.wikiTicketCalendar td.fill {
    background: #eee; border-color: #ccc;
    border-style: solid; border-width: 1px;
    }
table.wikiTicketCalendar div.milestone {
    font-size: 9px; background: #f7f7f0; border: 1px solid #d7d7d7;
    border-bottom-color: #999; text-align: left;
    }
table.wikiTicketCalendar a.day {
    width: 2em; height: 100%; margin: 0; border: 0px; padding: 0;
    color: #333; text-decoration: none;
    }
table.wikiTicketCalendar a.day_haspage {
    width: 2em; height: 100%; margin: 0; border: 0px; padding: 0;
    color: #b00 !important; text-decoration: none;
    }
table.wikiTicketCalendar a.day:hover {
    border-color: #eee; background-color: #eee; color: #000;
    }
table.wikiTicketCalendar div.open, span.open   {
    font-size: 9px; color: #000000;
    }
table.wikiTicketCalendar div.closed, span.closed {
    font-size: 9px; color: #777777; text-decoration: line-through;
    }
table.wikiTicketCalendar div.opendate_open, span.opendate_open {
    font-size: 9px; color: #000077;
    }
table.wikiTicketCalendar div.opendate_closed, span.opendate_closed {
    font-size: 9px; color: #000077; text-decoration: line-through;
    }
table.wikiTicketCalendar div.condense { background-color: #e5e5e5; }
table.wikiTicketCalendar div.opendate_condense { background-color: #cdcdfa; }

/* pure CSS style tooltip */
a.tip {
    position: relative; cursor: help;
    }
a.tip span { display: none; }
a.tip:hover span {
    display: block; z-index: 1;
    font-size: 0.75em; text-decoration: none;
    /* big left move because of z-index render bug in IE<8 */
    position: absolute; top: 0.8em; left: 6em;
    border: 1px solid #000; background-color: #fff; padding: 5px;
    }
-->
"""

        # create inline style definition as Genshi fragment
        styles = tag.style(Markup(styles))
        styles(type='text/css')

        # Build caption and optional navigation links
        buff = tag.caption()

        if showbuttons is True:
            # calendar navigation buttons
            nx = 'next'
            pv = 'prev'
            nav_pvY = self._mknav('&nbsp;&lt;&lt;', pv, month, year-1)
            nav_frM = self._mknav('&nbsp;&lt;&nbsp;', pv, frMonth, frYear)
            nav_pvM = self._mknav('&nbsp;&laquo;&nbsp;', pv, prevMonth,
                                                                 prevYear)
            nav_nxM = self._mknav('&nbsp;&raquo;&nbsp;', nx, nextMonth,
                                                                 nextYear)
            nav_ffM = self._mknav('&nbsp;&gt;&nbsp;', nx, ffMonth, ffYear)
            nav_nxY = self._mknav('&nbsp;&gt;&gt;', nx, month, year+1)

            # add buttons for going to previous months and year
            buff(nav_pvY, nav_frM, nav_pvM)

        # The caption will always be there.
        buff(tag.strong(to_unicode(format_date(self._mkdatetime(
                                               year, month), '%B %Y'))))

        if showbuttons is True:
            # add buttons for going to next months and year
            buff(nav_nxM, nav_ffM, nav_nxY)

        buff = tag.table(buff)
        width=":".join(['min-width', cal_width]) 
        buff(class_='wikiTicketCalendar', style=width)

        heading = tag.tr()
        heading(align='center')

        for day in calendar.weekheader(2).split()[:-2]:
            col = tag.th(day)
            col(class_='workday', scope='col')
            heading(col)
        for day in calendar.weekheader(2).split()[-2:]:
            col = tag.th(day)
            col(class_='weekend', scope='col')
            heading(col)

        heading = buff(tag.thead(heading))

        # Building main calendar table body
        buff = tag.tbody()
        for row in cal:
            line = tag.tr()
            line(align='right')
            for day in row:
                if not day:
                    cell = tag.td('')
                    cell(class_='fill')
                    line(cell)
                else:
                    # check for wikipage with name specified in
                    # 'wiki_page_format'
                    wiki = format_date(self._mkdatetime(year, month, day),
                                                         wiki_page_format)
                    url = self.env.href.wiki(wiki)
                    if WikiSystem(self.env).has_page(wiki):
                        a_class = "day_haspage"
                        title = "Go to page %s" % wiki
                    else:
                        a_class = "day"
                        url += "?action=edit"
                        # adding template name, if specified
                        if wiki_page_template != "":
                            url += "&template=" + wiki_page_template
                        title = "Create page %s" % wiki
                    if day == curr_day:
                        td_class = 'today'
                    else:
                        td_class = 'day'

                    cell = tag.a(tag.b(day), href=url)
                    cell(class_=a_class, title_=title)
                    cell = tag.td(cell)
                    cell(class_=td_class, valign='top')

                    day_dt = self._mkdatetime(year, month, day)
                    if uts:
                        day_ts = to_utimestamp(day_dt)
                        day_ts_eod = day_ts + 86399999999
                    else:
                        day_ts = to_timestamp(day_dt)
                        day_ts_eod = day_ts + 86399

                    # check for milestone(s) on that day
                    db = self.env.get_db_cnx()
                    cursor = db.cursor()
                    cursor.execute("""
                        SELECT name
                          FROM milestone
                         WHERE due >= %s and due <= %s
                    """, (day_ts, day_ts_eod))
                    while (1):
                        row = cursor.fetchone()
                        if row is None:
                            cell(tag.br())
                            break
                        else:
                            name = to_unicode(row[0])
                            url = self.env.href.milestone(name)
                            milestone = '* ' + name
                            milestone = tag.div(tag.a(milestone, href=url))
                            milestone(class_='milestone')

                            cell(milestone)

                    match = []
                    match_od = []
                    ticket_heap = tag('')
                    ticket_list = tag.div('')
                    ticket_list(align='left', class_='condense')

                    # get tickets with due date set to day
                    for t in self.tickets:
                        due = t.get(self.due_field_name)
                        if due is None or due in ['', '--']:
                            continue
                        else:
                            if self.due_field_fmt == 'ts':
                                if not isinstance(due, datetime.datetime):
                                    continue
                                else:
                                    if uts:
                                        due_ts = to_utimestamp(due)
                                    else:
                                        due_ts = to_timestamp(due)
                                if due_ts < day_ts or \
                                    due_ts > day_ts_eod:
                                    continue
                            else:
                                duedate = format_date(day_dt,
                                                      self.due_field_fmt)
                                if not due == duedate:
                                    continue

                        id = t.get('id')
                        ticket, short = self._gen_ticket_entry(t)
                        ticket_heap(ticket)
                        if not id in match:
                            if len(match) == 0:
                                ticket_list(short)
                            else:
                                ticket_list(', ', short)
                            match.append(id)

                    # optionally get tickets created on day
                    if show_t_open_dates is True:
                        ticket_od_list = tag.div('')
                        ticket_od_list(align='left',
                                       class_='opendate_condense')

                        for t in self.tickets:
                            if uts:
                                ticket_ts = to_utimestamp(t.get('time'))
                            else:
                                ticket_ts = to_timestamp(t.get('time'))
                            if ticket_ts < day_ts or ticket_ts > day_ts_eod:
                                continue

                            a_class = 'opendate_'
                            id = t.get('id')
                            ticket, short = self._gen_ticket_entry(t, a_class)
                            ticket_heap(ticket)
                            if not id in match:
                                if len(match_od) == 0:
                                    ticket_od_list(short)
                                else:
                                    ticket_od_list(', ', short)
                                match_od.append(id)

                    matches = len(match) + len(match_od)
                    if list_condense > 0 and matches >= list_condense:
                        if len(match_od) > 0:
                            if len(match) > 0:
                                ticket_list(', ')
                            ticket_list = tag(ticket_list, ticket_od_list)
                        line(cell(ticket_list))
                    else:
                        line(cell(ticket_heap))

            buff(line)

        buff = tag.div(heading(buff))
        if cal_width.startswith('+') is True:
            width=":".join(['width', cal_width]) 
            buff(class_='wikiTicketCalendar', style=width)
        else:
            buff(class_='wikiTicketCalendar')

        # Finally prepend prepared CSS styles
        buff = tag(styles, buff) 
        
        return buff
Example #9
0
    def expand_macro(self, formatter, name, arguments):

        self.ref = formatter
        self.tz_info = formatter.req.tz
        self.thistime = datetime.datetime.now(self.tz_info)

        # Parse arguments from macro invocation
        args, kwargs = parse_args(arguments, strict=False)

        # Find out whether use http param, current or macro param year/month
        http_param_year = formatter.req.args.get('year', '')
        http_param_month = formatter.req.args.get('month', '')

        if http_param_year == "":
            # not clicked on a prev or next button
            if len(args) >= 1 and args[0] <> "*":
                # year given in macro parameters
                year = int(args[0])
            else:
                # use current year
                year = self.thistime.year
        else:
            # year in http params (clicked by user) overrides everything
            year = int(http_param_year)

        if http_param_month == "":
            # not clicked on a prev or next button
            if len(args) >= 2 and args[1] <> "*":
                # month given in macro parameters
                month = int(args[1])
            else:
                # use current month
                month = self.thistime.month
        else:
            # month in http params (clicked by user) overrides everything
            month = int(http_param_month)

        showbuttons = True
        if len(args) >= 3 or kwargs.has_key('nav'):
            try:
                showbuttons = kwargs['nav'] in ["True", "true", "yes", "1"]
            except KeyError:
                showbuttons = args[2] in ["True", "true", "yes", "1"]

        wiki_page_format = "%Y-%m-%d"
        if len(args) >= 4 and args[3] != "*" or kwargs.has_key('wiki'):
            try:
                wiki_page_format = str(kwargs['wiki'])
            except KeyError:
                wiki_page_format = str(args[3])
        # Support relative paths in macro arguments for wiki page links.
        wiki_page_format = self._resolve_relative_name(wiki_page_format,
                                                       formatter.resource.id)

        list_condense = 0
        show_t_open_dates = True
        wiki_page_template = ""
        wiki_subpages = []

        # Read optional check plan.
        check = []
        if kwargs.has_key('check'):
            check = kwargs['check'].split('.')

        if name == 'WikiTicketCalendar':
            if len(args) >= 5 or kwargs.has_key('cdate'):
                try:
                    show_t_open_dates = kwargs['cdate'] in \
                                               ["True", "true", "yes", "1"]
                except KeyError:
                    show_t_open_dates = args[4] in \
                                               ["True", "true", "yes", "1"]

        # Optional page template to create new wiki pages.
        # The default (empty page) is used, if the template name is invalid.
        if len(args) >= 6 or kwargs.has_key('base'):
            try:
                wiki_page_template = kwargs['base']
            except KeyError:
                wiki_page_template = args[5]

        if name == 'WikiTicketCalendar':
            # TracQuery support for ticket selection
            query_args = "id!=0"
            if len(args) >= 7 or kwargs.has_key('query'):
                # prefer query arguments provided by kwargs
                try:
                    query_args = kwargs['query']
                except KeyError:
                    query_args = args[6]
            tickets = WikiCalendarTicketProvider(self.env)
            self.tickets = tickets.harvest(formatter.req, query_args)

            # compress long ticket lists
            if len(args) >= 8 or kwargs.has_key('short'):
                # prefer query arguments provided by kwargs
                try:
                    list_condense = int(kwargs['short'])
                except KeyError:
                    list_condense = int(args[7])

            # control calendar display width
            cal_width = "100%;"
            if len(args) >= 9 or kwargs.has_key('width'):
                # prefer query arguments provided by kwargs
                try:
                    cal_width = kwargs['width']
                except KeyError:
                    cal_width = args[8]

            # multiple wiki (sub)pages per day
            if kwargs.has_key('subpages'):
                wiki_subpages = kwargs['subpages'].split('|')

            # Can use this to change the day the week starts on,
            # but this is a system-wide setting.
            calendar.setfirstweekday(calendar.MONDAY)

        cal = calendar.monthcalendar(year, month)
        curr_day = None
        if year == self.thistime.year and month == self.thistime.month:
            curr_day = self.thistime.day

        # for prev/next navigation links
        prevMonth = month - 1
        nextMonth = month + 1
        nextYear = prevYear = year
        # check for year change (KISS version)
        if prevMonth == 0:
            prevMonth = 12
            prevYear -= 1
        if nextMonth == 13:
            nextMonth = 1
            nextYear += 1

        # for fast-forward/-rewind navigation links
        ffYear = frYear = year
        if month < 4:
            frMonth = month + 9
            frYear -= 1
        else:
            frMonth = month - 3
        if month > 9:
            ffMonth = month - 9
            ffYear += 1
        else:
            ffMonth = month + 3

        last_week_prevMonth = calendar.monthcalendar(prevYear, prevMonth)[-1]
        first_week_nextMonth = calendar.monthcalendar(nextYear, nextMonth)[0]

        # Switch to user's locale, if available.
        try:
            loc_req = str(formatter.req.locale)
        except AttributeError:
            # Available since in Trac 0.12 .
            loc_req = None
        if loc_req:
            loc = locale.getlocale()
            loc_prop = locale.normalize(loc_req)
            try:
                locale.setlocale(locale.LC_TIME, loc_prop)
            except locale.Error:
                try:
                    # Re-try with UTF-8 as last resort.
                    loc_prop = '.'.join([loc_prop.split('.')[0], 'utf8'])
                    locale.setlocale(locale.LC_TIME, loc_prop)
                except locale.Error:
                    loc_prop = None
            self.env.log.debug('Locale setting for calendar: ' + str(loc_prop))

        # Finally building the output
        # Begin with caption and optional navigation links
        buff = tag.tr()

        if showbuttons is True:
            # calendar navigation buttons
            nx = 'next'
            pv = 'prev'
            nav_pvY = self._mknav('&lt;&lt;', pv, month, year - 1)
            nav_frM = self._mknav('&nbsp;&lt;', pv, frMonth, frYear)
            nav_pvM = self._mknav('&nbsp;&laquo;', pv, prevMonth, prevYear)
            nav_nxM = self._mknav('&raquo;&nbsp;', nx, nextMonth, nextYear)
            nav_ffM = self._mknav('&gt;&nbsp;', nx, ffMonth, ffYear)
            nav_nxY = self._mknav('&gt;&gt;', nx, month, year + 1)

            # add buttons for going to previous months and year
            buff(nav_pvY, nav_frM, nav_pvM)

        # The caption will always be there.
        heading = tag.td(
            to_unicode(format_date(self._mkdatetime(year, month), '%B %Y')))
        buff = buff(heading(class_='y'))

        if showbuttons is True:
            # add buttons for going to next months and year
            buff(nav_nxM, nav_ffM, nav_nxY)
        buff = tag.caption(tag.table(tag.tbody(buff)))
        buff = tag.table(buff)
        if name == 'WikiTicketCalendar':
            if cal_width.startswith('+') is True:
                width = ":".join(['min-width', cal_width])
                buff(class_='wikitcalendar', style=width)
            else:
                buff(class_='wikitcalendar')
        if name == 'WikiCalendar':
            buff(class_='wiki-calendar')

        heading = tag.tr()
        heading(align='center')

        for day in calendar.weekheader(2).split()[:-2]:
            col = tag.th(to_unicode(day))
            col(class_='workday', scope='col')
            heading(col)
        for day in calendar.weekheader(2).split()[-2:]:
            col = tag.th(to_unicode(day))
            col(class_='weekend', scope='col')
            heading(col)

        heading = buff(tag.thead(heading))

        # Building main calendar table body
        buff = tag.tbody()
        w = -1
        for week in cal:
            w = w + 1
            line = tag.tr()
            line(align='right')
            d = -1
            for day in week:
                d = d + 1
                if day:
                    # check for wikipage with name specified in
                    # 'wiki_page_format'
                    wiki = format_date(self._mkdatetime(year, month, day),
                                       wiki_page_format)
                    if day == curr_day:
                        a_class = 'day today'
                        td_class = 'today'
                    else:
                        a_class = 'day'
                        td_class = 'day'

                    day_dt = self._mkdatetime(year, month, day)
                    if uts:
                        day_ts = to_utimestamp(day_dt)
                        day_ts_eod = day_ts + 86399999999
                    else:
                        day_ts = to_timestamp(day_dt)
                        day_ts_eod = day_ts + 86399

                    # check for milestone(s) on that day
                    db = self.env.get_db_cnx()
                    cursor = db.cursor()
                    cursor.execute(
                        """
                        SELECT name
                          FROM milestone
                         WHERE due >= %s and due <= %s
                    """, (day_ts, day_ts_eod))
                    milestones = tag()
                    for row in cursor:
                        if not a_class.endswith('milestone'):
                            a_class += ' milestone'
                        milestone = to_unicode(row[0])
                        url = self.env.href.milestone(milestone)
                        milestone = '* ' + milestone
                        milestones = tag(
                            milestones,
                            tag.div(tag.a(milestone, href=url),
                                    class_='milestone'))
                    day = tag.span(day)
                    day(class_='day')
                    if len(wiki_subpages) > 0:
                        pages = tag(day, Markup('<br />'))
                        for page in wiki_subpages:
                            label = tag(' ', page[0])
                            page = '/'.join([wiki, page])
                            url = self.env.href.wiki(page)
                            pages(
                                self._gen_wiki_links(page, label, 'subpage',
                                                     url, wiki_page_template,
                                                     check))
                    else:
                        url = self.env.href.wiki(wiki)
                        pages = self._gen_wiki_links(wiki, day, a_class, url,
                                                     wiki_page_template, check)
                    cell = tag.td(pages)
                    cell(class_=td_class, valign='top')
                    if name == 'WikiCalendar':
                        line(cell)
                    else:
                        if milestones:
                            cell(milestones)
                        else:
                            cell(tag.br())

                        match = []
                        match_od = []
                        ticket_heap = tag('')
                        ticket_list = tag.div('')
                        ticket_list(align='left', class_='condense')

                        # get tickets with due date set to day
                        for t in self.tickets:
                            due = t.get(self.tkt_due_field)
                            if due is None or due in ['', '--']:
                                continue
                            else:
                                if self.tkt_due_format == 'ts':
                                    if not isinstance(due, datetime.datetime):
                                        continue
                                    if uts:
                                        due_ts = to_utimestamp(due)
                                    else:
                                        due_ts = to_timestamp(due)
                                    if due_ts < day_ts or due_ts > day_ts_eod:
                                        continue
                                else:
                                    # Beware: Format might even be unicode str
                                    duedate = format_date(
                                        day_dt, str(self.tkt_due_format))
                                    if not due == duedate:
                                        continue

                            id = t.get('id')
                            ticket, short = self._gen_ticket_entry(t)
                            ticket_heap(ticket)
                            if not id in match:
                                if len(match) == 0:
                                    ticket_list(short)
                                else:
                                    ticket_list(', ', short)
                                match.append(id)

                        # optionally get tickets created on day
                        if show_t_open_dates is True:
                            ticket_od_list = tag.div('')
                            ticket_od_list(align='left',
                                           class_='opendate_condense')

                            for t in self.tickets:
                                if uts:
                                    ticket_ts = to_utimestamp(t.get('time'))
                                else:
                                    ticket_ts = to_timestamp(t.get('time'))
                                if ticket_ts < day_ts or \
                                        ticket_ts > day_ts_eod:
                                    continue

                                a_class = 'opendate_'
                                id = t.get('id')
                                ticket, short = self._gen_ticket_entry(
                                    t, a_class)
                                ticket_heap(ticket)
                                if not id in match:
                                    if len(match_od) == 0:
                                        ticket_od_list(short)
                                    else:
                                        ticket_od_list(', ', short)
                                    match_od.append(id)

                        matches = len(match) + len(match_od)
                        if list_condense > 0 and matches >= list_condense:
                            if len(match_od) > 0:
                                if len(match) > 0:
                                    ticket_list(', ')
                                ticket_list = tag(ticket_list, ticket_od_list)
                            line(cell(ticket_list))
                        else:
                            line(cell(ticket_heap))
                else:
                    if name == 'WikiCalendar':
                        if w == 0:
                            day = last_week_prevMonth[d]
                            wiki = format_date(
                                self._mkdatetime(prevYear, prevMonth, day),
                                wiki_page_format)
                        else:
                            day = first_week_nextMonth[d]
                            wiki = format_date(
                                self._mkdatetime(nextYear, nextMonth, day),
                                wiki_page_format)
                        url = self.env.href.wiki(wiki)
                        a_class = 'day adjacent_month'
                        pages = self._gen_wiki_links(wiki, day, a_class, url,
                                                     wiki_page_template)

                        cell = tag.td(pages)
                        cell(class_='day adjacent_month')
                        line(cell)
                    else:
                        cell = tag.td('')
                        cell(class_='day adjacent_month')
                        line(cell)
            buff(line)

        if loc_req and loc_prop:
            # We may have switched to users locale, resetting now.
            try:
                locale.setlocale(locale.LC_ALL, loc)
                self.env.log.debug('Locale setting restored: ' + str(loc))
            except locale.Error:
                pass

        buff = tag.div(heading(buff))
        if name == 'WikiTicketCalendar':
            if cal_width.startswith('+') is True:
                width = ":".join(['width', cal_width])
                buff(class_='wikitcalendar', style=width)
            else:
                buff(class_='wikitcalendar')
        if name == 'WikiCalendar':
            buff(class_='wiki-calendar')
        # Add common CSS stylesheet
        if self.internal_css and not self.ref.req.args.get('wikicalendar'):
            # Put definitions directly into the output.
            f = open('/'.join([self.htdocs_path, 'wikicalendar.css']), 'Ur')
            css = tag.style(Markup('<!--\n'), '\n'.join(f.readlines()),
                            Markup('-->\n'))(type="text/css")
            f.close()
            # Add hint to prevent multiple inclusions.
            self.ref.req.args['wikicalendar'] = True
            return tag(css, buff)
        elif not self.ref.req.args.get('wikicalendar'):
            add_stylesheet(self.ref.req, 'wikicalendar/wikicalendar.css')
        return buff
Example #10
0
    def expand_macro(self, formatter, name, arguments):
        """Returns macro content."""
        env = self.env
        req = formatter.req
        tz = req.tz

        # Parse arguments from macro invocation.
        args, kwargs = parse_args(arguments, strict=False)

        # Enable week number display regardless of argument position.
        week_pref = "w" in args and args.pop(args.index("w"))

        week_pref = week_pref and week_pref or kwargs.get("w")
        week_start = None
        week_num_start = None
        # Parse per-instance week calculation rules, if available.
        if week_pref:
            if ":" not in week_pref:
                # Treat undelimitted setting as week start.
                week_pref += ":"
            w_start, wn_start = week_pref.split(":")
            try:
                week_start = int(w_start)
            except ValueError:
                week_start = None
            else:
                week_start = week_start > -1 and week_start < 7 and week_start or None
            try:
                week_num_start = int(wn_start)
            except ValueError:
                week_num_start = None
            else:
                week_num_start = week_num_start in (1, 4, 7) and week_num_start or None

        # Respect user's locale, if available.
        try:
            locale = Locale.parse(str(req.locale))
        except (AttributeError, UnknownLocaleError):
            # Attribute 'req.locale' vailable since Trac 0.12.
            locale = None
        if has_babel:
            if locale:
                if not locale.territory:
                    # Search first locale, which has the same `language` and
                    # territory in preferred languages.
                    for l in req.languages:
                        l = l.replace("-", "_").lower()
                        if l.startswith(locale.language.lower() + "_"):
                            try:
                                l = Locale.parse(l)
                                if l.territory:
                                    locale = l
                                    break  # first one rules
                            except UnknownLocaleError:
                                pass
                if not locale.territory and locale.language in LOCALE_ALIASES:
                    locale = Locale.parse(LOCALE_ALIASES[locale.language])
            else:
                # Default fallback.
                locale = Locale("en", "US")
            env.log.debug("Locale setting for wiki calendar: %s" % locale.get_display_name("en"))
        if not week_start:
            if week_pref and week_pref.lower().startswith("iso"):
                week_start = 0
                week_num_start = 4
            elif has_babel:
                week_start = locale.first_week_day
            else:
                import calendar

                week_start = calendar.firstweekday()
        # ISO calendar will remain as default.
        if not week_num_start:
            if week_start == 6:
                week_num_start = 1
            else:
                week_num_start = 4
        env.log.debug(
            "Effective settings: first_week_day=%s, " "1st_week_of_year_rule=%s" % (week_start, week_num_start)
        )

        # Find year and month of interest.
        year = req.args.get("year")
        # Not clicked on any previous/next button, next look for macro args.
        if not year and len(args) >= 1 and args[0] != "*":
            year = args[0]
        year = year and year.isnumeric() and int(year) or None

        month = req.args.get("month")
        # Not clicked on any previous/next button, next look for macro args.
        if not month and len(args) >= 2 and args[1] != "*":
            month = args[1]
        month = month and month.isnumeric() and int(month) or None

        now = datetime.now(tz)
        # Force offset from start-of-day to avoid a false 'today' marker,
        # but use it only on request of different month/year.
        now.replace(second=1)
        today = None
        if month and month != now.month:
            today = now.replace(month=month)
        if year and year != now.year:
            today = today and today.replace(year=year) or now.replace(year=year)
        # Use current month and year, if nothing else has been requested.
        if not today:
            today = now.replace(hour=0, minute=0, second=0, microsecond=0)

        showbuttons = True
        if len(args) >= 3 or kwargs.has_key("nav"):
            try:
                showbuttons = kwargs["nav"] in _TRUE_VALUES
            except KeyError:
                showbuttons = args[2] in _TRUE_VALUES

        wiki_page_format = "%Y-%m-%d"
        if len(args) >= 4 and args[3] != "*" or kwargs.has_key("wiki"):
            try:
                wiki_page_format = str(kwargs["wiki"])
            except KeyError:
                wiki_page_format = str(args[3])
        # Support relative paths in macro arguments for wiki page links.
        wiki_page_format = resolve_relative_name(wiki_page_format, formatter.resource.id)

        list_condense = 0
        show_t_open_dates = True
        wiki_subpages = []

        # Read optional check plan.
        check = []
        if kwargs.has_key("check"):
            check = kwargs["check"].split(".")

        if name == "WikiTicketCalendar":
            if len(args) >= 5 or kwargs.has_key("cdate"):
                try:
                    show_t_open_dates = kwargs["cdate"] in _TRUE_VALUES
                except KeyError:
                    show_t_open_dates = args[4] in _TRUE_VALUES

            # TracQuery support for ticket selection
            query_args = "id!=0"
            if len(args) >= 7 or kwargs.has_key("query"):
                # prefer query arguments provided by kwargs
                try:
                    query_args = kwargs["query"]
                except KeyError:
                    query_args = args[6]
            provider = WikiCalendarTicketProvider(env)
            tickets = provider.harvest(req, query_args)

            # compress long ticket lists
            if len(args) >= 8 or kwargs.has_key("short"):
                # prefer query arguments provided by kwargs
                try:
                    list_condense = int(kwargs["short"])
                except KeyError:
                    list_condense = int(args[7])

            # control calendar display width
            cal_width = "100%;"
            if len(args) >= 9 or kwargs.has_key("width"):
                # prefer query arguments provided by kwargs
                try:
                    cal_width = kwargs["width"]
                except KeyError:
                    cal_width = args[8]

            # multiple wiki (sub)pages per day
            if kwargs.has_key("subpages"):
                wiki_subpages = kwargs["subpages"].split("|")

        # Prepare datetime objects for previous/next navigation link creation.
        prev_year = month_offset(today, -12)
        prev_quarter = month_offset(today, -3)
        prev_month = month_offset(today, -1)
        next_month = month_offset(today, 1)
        next_quarter = month_offset(today, 3)
        next_year = month_offset(today, 12)

        # Find first and last calendar day, probably in last/next month,
        # using datetime objects exactly at start-of-day here.
        # Note: Calendar days are numbered 0 (Mo) - 6 (Su).
        first_day_month = today.replace(day=1, second=0)
        first_day = first_day_month - timedelta(week_index(first_day_month, week_start))
        last_day_month = next_month.replace(day=1) - timedelta(1)
        if ((last_day_month - first_day).days + 1) % 7 > 0:
            last_day = last_day_month + timedelta(7 - ((last_day_month - first_day).days + 1) % 7)
        else:
            last_day = last_day_month

        # Finally building the output now.
        # Begin with caption and optional navigation links.
        buff = tag.tr()

        if showbuttons is True:
            # Create calendar navigation buttons.
            nx = "next"
            pv = "prev"
            nav_pv_y = _nav_link(req, "&lt;&lt;", pv, prev_year, locale)
            nav_pv_q = _nav_link(req, "&nbsp;&laquo;", pv, prev_quarter, locale)
            nav_pv_m = _nav_link(req, "&nbsp;&lt;", pv, prev_month, locale)
            nav_nx_m = _nav_link(req, "&gt;&nbsp;", nx, next_month, locale)
            nav_nx_q = _nav_link(req, "&raquo;&nbsp;", nx, next_quarter, locale)
            nav_nx_y = _nav_link(req, "&gt;&gt;", nx, next_year, locale)

            # Add buttons for going to previous months and year.
            buff(nav_pv_y, nav_pv_q, nav_pv_m)

        # The caption will always be there.
        if has_babel:
            heading = tag.td(format_datetime(today, "MMMM y", locale=locale))
        else:
            heading = tag.td(format_date(today, "%B %Y"))
        buff = buff(heading(class_="y"))

        if showbuttons is True:
            # Add buttons for going to next months and year.
            buff(nav_nx_m, nav_nx_q, nav_nx_y)
        buff = tag.caption(tag.table(tag.tbody(buff)))
        buff = tag.table(buff)
        if name == "WikiTicketCalendar":
            if cal_width.startswith("+") is True:
                width = ":".join(["min-width", cal_width])
                buff(class_="wikitcalendar", style=width)
            else:
                buff(class_="wikitcalendar")
        if name == "WikiCalendar":
            buff(class_="wiki-calendar")

        heading = tag.tr()
        heading(align="center")
        if week_pref:
            # Add an empty cell matching the week number column below.
            heading(tag.th())

        day_names = [(idx, day_name) for idx, day_name in get_day_names("abbreviated", "format", locale).iteritems()]
        # Read day names after shifting into correct position.
        for idx, name_ in day_names[week_start:7] + day_names[0:week_start]:
            col = tag.th(name_)
            if has_babel:
                weekend = idx >= locale.weekend_start and idx <= locale.weekend_end
            else:
                weekend = idx > 4
            col(class_=("workday", "weekend")[weekend], scope="col")
            heading(col)
        heading = buff(tag.thead(heading))

        # Building main calendar table body
        buff = tag.tbody()
        day = first_day
        while day.date() <= last_day.date():
            # Insert a new row for every week.
            if (day - first_day).days % 7 == 0:
                line = tag.tr()
                line(align="right")
                if week_pref:
                    cell = tag.td(week_num(env, day, week_start, week_num_start))
                    line(cell(class_="week"))
            if not (day < first_day_month or day > last_day_month):
                wiki = format_date(day, wiki_page_format)
                if day == today:
                    a_class = "day today"
                    td_class = "today"
                else:
                    a_class = "day"
                    td_class = "day"

                if uts:
                    day_ts = to_utimestamp(day)
                    day_ts_eod = day_ts + 86399999999
                else:
                    day_ts = to_timestamp(day)
                    day_ts_eod = day_ts + 86399

                # Check for milestone(s) on that day.
                db = env.get_db_cnx()
                cursor = db.cursor()
                cursor.execute(
                    """
                    SELECT name
                      FROM milestone
                     WHERE due >= %s and due <= %s
                """,
                    (day_ts, day_ts_eod),
                )
                milestones = tag()
                for row in cursor:
                    if not a_class.endswith("milestone"):
                        a_class += " milestone"
                    milestone = to_unicode(row[0])
                    url = env.href.milestone(milestone)
                    milestone = "* " + milestone
                    milestones = tag(milestones, tag.div(tag.a(milestone, href=url), class_="milestone"))
                label = tag.span(day.day)
                label(class_="day")
                # Generate wiki page links with name specified in
                # 'wiki_page_format', and check their existence.
                if len(wiki_subpages) > 0:
                    pages = tag(label, Markup("<br />"))
                    for page in wiki_subpages:
                        label = tag(" ", page[0])
                        page = "/".join([wiki, page])
                        pages(self._wiki_link(req, args, kwargs, page, label, "subpage", check))
                else:
                    pages = self._wiki_link(req, args, kwargs, wiki, label, a_class, check)
                cell = tag.td(pages)
                cell(class_=td_class, valign="top")
                if name == "WikiCalendar":
                    line(cell)
                else:
                    if milestones:
                        cell(milestones)
                    else:
                        cell(tag.br())

                    match = []
                    match_od = []
                    ticket_heap = tag("")
                    ticket_list = tag.div("")
                    ticket_list(align="left", class_="condense")

                    # Get tickets with due date set to day.
                    for t in tickets:
                        due = t.get(self.tkt_due_field)
                        if due is None or due in ("", "--"):
                            continue
                        else:
                            if self.tkt_due_format == "ts":
                                if not isinstance(due, datetime):
                                    continue
                                if uts:
                                    due_ts = to_utimestamp(due)
                                else:
                                    due_ts = to_timestamp(due)
                                if due_ts < day_ts or due_ts > day_ts_eod:
                                    continue
                            else:
                                # Beware: Format might even be unicode string,
                                # but str is required by the function.
                                duedate = format_date(day, str(self.tkt_due_format))
                                if not due == duedate:
                                    continue

                        tkt_id = t.get("id")
                        ticket, short = _ticket_links(env, formatter, t)
                        ticket_heap(ticket)
                        if not tkt_id in match:
                            if len(match) == 0:
                                ticket_list(short)
                            else:
                                ticket_list(", ", short)
                            match.append(tkt_id)

                    # Optionally, get tickets created on day too.
                    if show_t_open_dates is True:
                        ticket_od_list = tag.div("")
                        ticket_od_list(align="left", class_="opendate_condense")

                        for t in tickets:
                            if uts:
                                ticket_ts = to_utimestamp(t.get("time"))
                            else:
                                ticket_ts = to_timestamp(t.get("time"))
                            if ticket_ts < day_ts or ticket_ts > day_ts_eod:
                                continue

                            a_class = "opendate_"
                            tkt_id = t.get("id")
                            ticket, short = _ticket_links(env, formatter, t, a_class)
                            ticket_heap(ticket)
                            if not tkt_id in match:
                                if len(match_od) == 0:
                                    ticket_od_list(short)
                                else:
                                    ticket_od_list(", ", short)
                                match_od.append(tkt_id)

                    matches = len(match) + len(match_od)
                    if list_condense > 0 and matches >= list_condense:
                        if len(match_od) > 0:
                            if len(match) > 0:
                                ticket_list(", ")
                            ticket_list = tag(ticket_list, ticket_od_list)
                        line(cell(ticket_list))
                    else:
                        line(cell(ticket_heap))
            else:
                if name == "WikiCalendar":
                    wiki = format_date(day, wiki_page_format)
                    a_class = "day adjacent_month"
                    pages = self._wiki_link(req, args, kwargs, wiki, day.day, a_class)
                    cell = tag.td(pages, class_="day adjacent_month")
                    line(cell)
                else:
                    cell = tag.td("", class_="day adjacent_month")
                    line(cell)
            # Append completed week rows.
            if (day - first_day).days % 7 == 6:
                buff(line)
            day += timedelta(1)

        buff = tag.div(heading(buff))
        if name == "WikiTicketCalendar":
            if cal_width.startswith("+") is True:
                width = ":".join(["width", cal_width])
                buff(class_="wikitcalendar", style=width)
            else:
                buff(class_="wikitcalendar")
        if name == "WikiCalendar":
            buff(class_="wiki-calendar")
        # Add common CSS stylesheet.
        if self.internal_css and not req.args.get("wikicalendar"):
            # Put definitions directly into the output.
            f = open("/".join([self.htdocs_path, "wikicalendar.css"]), "Ur")
            css = tag.style(Markup("<!--\n"), "\n".join(f.readlines()), Markup("-->\n"))(type="text/css")
            f.close()
            # Add hint to prevent multiple inclusions.
            req.args["wikicalendar"] = True
            return tag(css, buff)
        elif not req.args.get("wikicalendar"):
            add_stylesheet(req, "wikicalendar/wikicalendar.css")
        return buff
Example #11
0
    def expand_macro(self, formatter, name, args):
        """
        Called by the formatter to render the parsed
        wiki text.
        """

        def to_html(text):
            """
            Format the parsed text from the rows into HTML.
            """
            if not text:
                return ''

            # simple check to determine whether format_to_html or 
            # format_to_oneliner should be used. If there are multiple
            # lines, use format_to_html, otherwise use format_to_oneliner

            stripped_text = text.strip()
            splitlines = stripped_text.splitlines()

            if len(splitlines) > 1:
                formatted_text = format_to_html(self.env, formatter.context, text)
            else:
                formatted_text = '<br />'.join( [format_to_oneliner(self.env, formatter.context, line) \
                    for line in text.splitlines()] ) 
            return Markup( formatted_text )

        if not args:
            return Markup()

        # use the formatter to find the TablePluginStyles
        if not formatter.wiki.has_page('TablePluginStyles'):
            # if our
            build_table_plugin_styles_page(self.env)

        if formatter.wiki.has_page('TablePluginStyles'):
            # at this point, the new style page should exist
            # so use the styles defined there.
            config_args = self._parse_wiki_style_page(self.env)
        else:
            # nice error handling in here possible incase
            # the page cannot be created for whatever reason?
            pass

        config_table_styles, config_css_styles = self._parse_styles(config_args)

        args_table_styles, args_css_styles = self._parse_styles(args)
        global_css_styles = dict(config_css_styles.items() + args_css_styles.items())

        column_list = []
        row_count = 0

        row_dict = {}
        row_list = []

        heading = False
        body_name = ''
        heading_set = False
        body_set_first = False
        first_row_as_header = False
        is_row = False

        table_id = ''
        table_data = ''

        table_style = ''
        column_style_dict = {}

        for attribute in self._parse_args(args):
            if 'table' in attribute:
                # get the table id to use
                table_id = attribute['table']['name']
                table_data = attribute['table']['data']
                table_style = attribute['table']['style'].replace('@', '')
            elif 'css' in attribute:
                pass
            elif 'column' in attribute:
                column_name = attribute['column']['name']
                column_list.append(column_name)
                if attribute['column']['style']:
                    column_style_dict[column_name] = attribute['column']['style']
            elif 'header' in attribute:
                heading = True
                heading_set = True
            elif 'body' in attribute:
                body_name = str(uuid4())
                if not heading_set and not first_row_as_header:
                    body_set_first = True
                heading = False
            elif 'row' in attribute:
                is_row = True
                row_count = 0
                row_style = attribute['row']['style']
            else:
                if not heading_set and not body_set_first:
                    first_row_as_header = True
                for key, value in attribute.items():
                    value['heading'] = heading
                    if body_name:
                        value['body_name'] = body_name
                    if is_row:
                        # if its a row, apply row style
                        original_style = value['style']
                        value['style'] = ' '.join([original_style, row_style])
                if row_count == (len(column_list) - 1):
                    row_dict.update(attribute)
                    row_list.append(row_dict)
                    row_dict = {}
                    row_count = 0
                    is_row = False
                else:
                    row_dict.update(attribute)
                    row_count += 1

        thead = tag.thead()
        for row in row_list:
            if body_set_first:
                break
            header_row = tag.tr()
            for column in column_list:
                header_cell = ''
                if row[column]['heading'] or first_row_as_header:
                    header_cell = tag.th()
                    header_cell(to_html(row[column]['data']), class_=row[column]['style'].replace('@', ''))
                    header_row(header_cell)
            if header_cell:
                thead(header_row)
            if first_row_as_header:
                break

        if table_style:
            table_id = table_style
            table_data = config_table_styles[table_style]['data']


        css_data = self._build_css_template(global_css_styles)

        full_style_data = ''
        full_style_data += table_data
        full_style_data += css_data

        tstyle = tag.style()
        tstyle = tag.style(full_style_data)

        tbody = tag.tbody()
        body_name = ''
        body_set = False

        tbody_list = []
        for row in row_list:
            if first_row_as_header:
                heading_set = False
                first_row_as_header = False
                continue
            trow = tag.tr()
            tcol = ''
            for column in column_list:
                if row[column]['heading']:
                    continue
                if row[column]['body_name'] == body_name and not body_set:
                    body_set = True
                elif row[column]['body_name'] != body_name and not body_set:
                    body_name = row[column]['body_name']
                    body_set = True
                elif row[column]['body_name'] != body_name and body_set:
                    tbody_list.append(tbody)
                    tbody = tag.tbody()
                    trow = tag.tr()
                    body_name = row[column]['body_name']
                tcol = tag.td()
                # if there is a column style available,
                # add it to what is already there
                formatted_column_style = ''
                if column in column_style_dict:
                    column_style = column_style_dict[column].replace('@', '')
                    formatted_column_style = column_style.replace(';', ' ')
                class_styles = row[column]['style'].replace('@', '')
                formatted_class_styles = class_styles.replace(';', ' ')
                formatted_item = ' '.join([formatted_class_styles, formatted_column_style])
                tcol(to_html(row[column]['data']), class_=formatted_item)
                trow(tcol)
            if tcol:
                tbody(trow)
        tbody_list.append(tbody)

        return tag.table([tstyle, thead, tbody_list], class_=table_id)