Ejemplo n.º 1
0
    def _validate_common(self, req, biff_values):
        name = req.args.get('name')
        cc = req.args.get('cc')
        filename = req.args.get('filename')

        if not (name and filename):
            add_warning(req, _('Name and Filename is required.'))
            return False

        if re.search(WHITESPACE_PATTERN, name):
            add_warning(req, _('Whitespace is not allowed for the name.'))
            return False

        if cc:
            all_users = [_u for _u, __, __ in self.env.get_known_users()]
            cc_users = [_u.strip() for _u in cc.split(',')]
            for user in cc_users:
                if user not in all_users:
                    _msg = _("The user '%(user)s' is not existed.", user=user)
                    add_warning(req, _msg)
                    return False

        biff_names = map(lambda x: x['name'], biff_values)
        if name in biff_names:
            add_warning(req, _('The name is already used.'))
            return False

        multiple_fields = ['filename']
        for field_name in multiple_fields:
            if not self._validate_multiple_field(req, field_name, biff_values):
                return False

        return True
Ejemplo n.º 2
0
    def validate_ticket(self, req, ticket):
        action = req.args.get('action')

        if action in self.opt_skip_validation:
            return

        if action == 'resolve':

            for parent, child in self.env.db_query(
                    """
                    SELECT parent, child FROM subtickets WHERE parent=%s
                    """, (ticket.id, )):
                if Ticket(self.env, child)['status'] != 'closed':
                    yield None, _("""Cannot close/resolve because child
                         ticket #%(child)s is still open""",
                                  child=child)

        elif action == 'reopen':
            ids = set(NUMBERS_RE.findall(ticket['parents'] or ''))
            for id in ids:
                if Ticket(self.env, id)['status'] == 'closed':
                    msg = _(
                        "Cannot reopen because parent ticket #%(id)s "
                        "is closed",
                        id=id)
                    yield None, msg
Ejemplo n.º 3
0
 def id_is_valid(num):
     try:
         return 0 < int(num) <= 1L << 31
     except ValueError:
         raise ResourceNotFound(
             _("TracForm %(form_id)s does not exist.", form_id=num),
             _("Invalid form number"))
Ejemplo n.º 4
0
def _render_change(old, new):
    rendered = None
    if old and not new:
        rendered = tag(
            Markup(_("%(value)s reset to default value", value=tag.em(old))))
    elif new and not old:
        rendered = tag(
            Markup(_("from default value set to %(value)s",
                     value=tag.em(new))))
    elif old and new:
        if len(old) < 20 and len(new) < 20:
            rendered = tag(
                Markup(
                    _("changed from %(old)s to %(new)s",
                      old=tag.em(old),
                      new=tag.em(new))))
        else:
            nbsp = tag.br()
            # TRANSLATOR: same as before, but with additional line breaks
            rendered = tag(
                Markup(
                    _("changed from %(old)s to %(new)s",
                      old=tag.em(nbsp, old),
                      new=tag.em(nbsp, new))))
    return rendered
Ejemplo n.º 5
0
 def post_process_request(self, req, template, data, content_type):
     env = self.env
     page = req.path_info
     realm, resource_id = resource_from_page(env, page)
     # break (recursive) search for form in forms realm
     if tfpageRE.match(page) == None and resource_id is not None:
         if page == '/wiki' or page == '/wiki/':
             page = '/wiki/WikiStart'
         form = Form(env, realm, resource_id)
         if 'FORM_VIEW' in req.perm(form.resource):
             if len(form.siblings) == 0:
                 # no form record found for this parent resource
                 href = req.href.form()
                 return (template, data, content_type)
             elif form.resource.id is not None:
                 # single form record found
                 href = req.href.form(form.resource.id)
             else:
                 # multiple form records found
                 href = req.href.form(action='select', realm=realm,
                                      resource_id=resource_id)
             add_ctxtnav(req, _("Form details"), href=href,
                         title=_("Review form data"))
     elif page.startswith('/form') and not resource_id == '':
         form = Form(env, form_id=resource_id)
         parent = form.resource.parent
         if len(form.siblings) > 1:
             href = req.href.form(action='select', realm=parent.realm,
                                  resource_id=parent.id)
             add_ctxtnav(req, _("Back to forms list"), href=href)
     return (template, data, content_type)
Ejemplo n.º 6
0
    def filter_stream(self, req, method, filename, stream, data):
        
        if req.path_info.startswith('/ticket/'):
            div = None
            if 'ticket' in data:
                # get parents data
                ticket = data['ticket']
                # title
                div = tag.div(class_='description')
                if ticket['status'] != 'closed':
                    link = tag.a(_('add'),
                        href=req.href.newticket(parents=ticket.id),
                        title=_('Create new child ticket'))
                    link = tag.span('(', link, ')', class_='addsubticket')
                else:
                    link = None
                div.append(tag.h3(_('Subtickets '), link))

            if 'subtickets' in data:
                # table
                tbody = tag.tbody()
                div.append(tag.table(tbody, class_='subtickets'))

                # tickets
                def _func(children, depth=0):
                    for id in sorted(children, key=lambda x: int(x)):
                        ticket = Ticket(self.env, id)

                        # 1st column
                        attrs = {'href': req.href.ticket(id)}
                        if ticket['status'] == 'closed':
                            attrs['class_'] = 'closed'
                        link = tag.a('#%s' % id, **attrs)
                        summary = tag.td(link, ': %s' % ticket['summary'],
                            style='padding-left: %dpx;' % (depth * 15))
                        # 2nd column
                        type = tag.td(ticket['type'])
                        # 3rd column
                        status = tag.td(ticket['status'])
                        # 4th column
                        href = req.href.query(status='!closed',
                                              owner=ticket['owner'])
                        owner = tag.td(tag.a(ticket['owner'], href=href))

                        tbody.append(tag.tr(summary, type, status, owner))
                        if depth >= 0: 
                            _func(children[id], depth + 1)

                if self.recursion:
                    _func(data['subtickets'])
                else:
                    _func(data['subtickets'],-1)

            if div:
                add_stylesheet(req, 'subtickets/css/subtickets.css')
                stream |= Transformer('.//div[@id="ticket"]').append(div)

        return stream
Ejemplo n.º 7
0
    def filter_stream(self, req, method, filename, stream, data):
        if req.path_info.startswith('/ticket/'):
            div = None
            if 'ticket' in data:
                # get parents data
                ticket = data['ticket']
                # title
                div = tag.div(class_='description')
                if ticket['status'] != 'closed':
                    link = tag.a(_('add'),
                                 href=req.href.newticket(parents=ticket.id),
                                 title=_('Create new child ticket'))
                    link = tag.span('(', link, ')', class_='addsubticket')
                else:
                    link = None
                div.append(tag.h3(_('Subtickets '), link))

            if 'subtickets' in data:
                # table
                tbody = tag.tbody()
                div.append(tag.table(tbody, class_='subtickets'))

                # tickets
                def _func(children, depth=0):
                    for id in sorted(children, key=lambda x: int(x)):
                        ticket = Ticket(self.env, id)

                        # 1st column
                        attrs = {'href': req.href.ticket(id)}
                        if ticket['status'] == 'closed':
                            attrs['class_'] = 'closed'
                        link = tag.a('#%s' % id, **attrs)
                        summary = tag.td(link,
                                         ': %s' % ticket['summary'],
                                         style='padding-left: %dpx;' %
                                         (depth * 15))
                        # 2nd column
                        type = tag.td(ticket['type'])
                        # 3rd column
                        status = tag.td(ticket['status'])
                        # 4th column
                        href = req.href.query(status='!closed',
                                              owner=ticket['owner'])
                        owner = tag.td(tag.a(ticket['owner'], href=href))

                        tbody.append(tag.tr(summary, type, status, owner))
                        _func(children[id], depth + 1)

                _func(data['subtickets'])

            if div:
                add_stylesheet(req, 'subtickets/css/subtickets.css')
                stream |= Transformer('.//div[@id="ticket"]').append(div)

        return stream
Ejemplo n.º 8
0
    def post_process_request(self, req, template, data, content_type):
        if req.path_info.startswith('/ticket/'):
            # In case of an invalid ticket, the data is invalid
            if not data:
                return template, data, content_type
            tkt = data['ticket']
            self.pm.check_component_enabled(self, pid=tkt.pid)
            links = TicketLinks(self.env, tkt)

            # Add link to depgraph if needed
            if links:
                add_ctxtnav(req, _('Depgraph'), req.href.depgraph(get_resource_url(self.env, tkt.resource)))

            for change in data.get('changes', {}):
                if not change.has_key('fields'):
                    continue
                for field, field_data in change['fields'].iteritems():
                    if field in self.fields:
                        vals = {}
                        for i in ('new', 'old'):
                            if isinstance(field_data[i], basestring):
                                val = field_data[i].strip()
                            else:
                                val = ''
                            if val:
                                vals[i] = set([int(n) for n in val.split(',')])
                            else:
                                vals[i] = set()
                        add = vals['new'] - vals['old']
                        sub = vals['old'] - vals['new']
                        elms = tag()
                        if add:
                            elms.append(
                                tag.em(u', '.join([unicode(n) for n in sorted(add)]))
                            )
                            elms.append(u' added')
                        if add and sub:
                            elms.append(u'; ')
                        if sub:
                            elms.append(
                                tag.em(u', '.join([unicode(n) for n in sorted(sub)]))
                            )
                            elms.append(u' removed')
                        field_data['rendered'] = elms

        #add a link to generate a dependency graph for all the tickets in the milestone
        if req.path_info.startswith('/milestone/'):
            if not data or not 'milestone' in data:
                return template, data, content_type
            milestone=data['milestone']
            self.pm.check_component_enabled(self, pid=milestone.pid)
            add_ctxtnav(req, _('Depgraph'), req.href.depgraph(get_resource_url(self.env, milestone.resource)))


        return template, data, content_type
Ejemplo n.º 9
0
 def _gen_wiki_links(self, wiki, label, a_class, url, wiki_page_template):
     if WikiSystem(self.env).has_page(wiki.lstrip('/')):
         a_class += " page"
         title = _("Go to page %s") % wiki
     else:
         url += "?action=edit"
         # adding template name, if specified
         if wiki_page_template != "":
             url += "&template=" + wiki_page_template
         title = _("Create page %s") % wiki
     link = tag.a(tag(label), href=url)
     link(class_=a_class, title_=title)
     return link
Ejemplo n.º 10
0
 def validate_ticket(self, req, ticket, action):
     if not ticket.exists: # new ticket
         return
     if not action:
         yield None, _('Valid ticket action must be provided to validate ticket dependencies')
         return
     syllabus_id = ticket.syllabus_id
     actions = self.check_actions.syllabus(syllabus_id)
     if action['alias'] in actions:
         links = TicketLinks(self.env, ticket)
         for i in links.blocked_by:
             if Ticket(self.env, i)['status'] != 'closed':
                 yield None, _('Ticket #%(id)s is blocking this ticket', id=i)
Ejemplo n.º 11
0
    def validate_ticket(self, req, ticket):
        action = req.args.get('action')
        if action == 'resolve':
            db = self.env.get_db_cnx()
            cursor = db.cursor()
            cursor.execute("SELECT parent, child FROM subtickets WHERE parent=%s", [str(ticket.id)] )

            for parent, child in cursor:
                if Ticket(self.env, child)['status'] != 'closed':
                    yield None, _('Child ticket #%s has not been closed yet') % child

        elif action == 'reopen':
            ids = set(NUMBERS_RE.findall(ticket['parents'] or ''))
            for id in ids:
                if Ticket(self.env, id)['status'] == 'closed':
                    yield None, _('Parent ticket #%s is closed') % id
Ejemplo n.º 12
0
 def get_tracform_ids(self, src, cursor=None):
     """Returns all child forms of resource specified by parent realm and
     parent id as a list of tuples (form_id and corresponding subcontext).
     """
     cursor = self.get_cursor(cursor)
     sql = """
         SELECT  id,
                 subcontext
         FROM    forms
         WHERE   realm = %s
             AND resource_id = %s
         """
     ids = []
     results = cursor(sql, *src)
     if results is not None:
         for form_id, subcontext in results:
             ids.append(tuple([int(form_id), subcontext]))
     else:
         raise ResourceNotFound(
             _("""No data recorded for a TracForms form in
                   %(realm)s:%(parent_id)s
                   """,
               realm=realm,
               parent_id=resource_id))
     return ids
Ejemplo n.º 13
0
    def __init__(self):
        # bind 'wikicalendar' catalog to the specified locale directory
        locale_dir = resource_filename(__name__, 'locale')
        add_domain(self.env.path, locale_dir)

        # Read options from Trac configuration system, adjustable in trac.ini.
        #  [wiki] section
        self.sanitize = True
        if self.config.getbool('wiki', 'render_unsafe_content') is True:
            self.sanitize = False

        # TRANSLATOR: Keep macro doc style formatting here, please.
        self.doc_calendar = _(
    """Inserts a small calendar where each day links to a wiki page whose name
    matches `wiki-page-format`. The current day is highlighted, and days with
    Milestones are marked in bold. This version makes heavy use of CSS for
    formatting.
    
    Usage:
    {{{
    [[WikiCalendar([year, [month, [show-buttons, [wiki-page-format]]]])]]
    }}}
    
    Arguments:
     1. `year` (4-digit year) - defaults to `*` (current year)
     1. `month` (2-digit month) - defaults to `*` (current month)
     1. `show-buttons` (boolean) - defaults to `true`
     1. `wiki-page-format` (string) - defaults to `%Y-%m-%d`
    
    Examples:
    {{{
    [[WikiCalendar(2006,07)]]
    [[WikiCalendar(2006,07,false)]]
    [[WikiCalendar(*,*,true,Meeting-%Y-%m-%d)]]
    [[WikiCalendar(2006,07,false,Meeting-%Y-%m-%d)]]
    }}}
    """)
        self.doc_ticketcalendar = _(
    """Display Milestones and Tickets in a calendar view.

    displays a calendar, the days link to:
     - milestones (day in bold) if there is one on that day
     - a wiki page that has wiki_page_format (if exist)
     - create that wiki page if it does not exist
     - use page template (if exist) for new wiki page
    """)
Ejemplo n.º 14
0
    def update_ticket_field(self, authname, old_value, new_value):
        ticket_ids = self._get_ticket_ids(old_value)
        if not ticket_ids:
            return

        comment = _('Updated File Biff field value by Trac administrator.')
        fb_methodcaller = methodcaller('update', old_value, new_value)
        self._update_field(authname, comment, ticket_ids, fb_methodcaller)
Ejemplo n.º 15
0
 def __init__(self, env, form_resource_or_parent_realm=None,
              parent_id=None, subcontext=None, form_id=None, version=None):
     self.env = env
     # prepare db access
     self.forms = FormSystem(env)
     self.realm = 'form'
     self.subcontext = subcontext
     self.siblings = []
     # DEVEL: support for handling form revisions not implemented yet
     if isinstance(form_resource_or_parent_realm, Resource):
         self.resource = form_resource_or_parent_realm
         parent = self.resource.parent
         if self.siblings == []:
             self._get_siblings(parent.realm, parent.id)
     else:
         parent_realm = form_resource_or_parent_realm
         if form_id not in [None, ''] and self.id_is_valid(form_id):
             self.id = int(form_id)
         else:
             self.id = None
         if self.id is not None and (parent_realm is None or \
                 parent_id is None or subcontext is None):
             # get complete context, required as resource parent
             ctxt = self.forms.get_tracform_meta(self.id)[1:4]
             parent_realm = ctxt[0]
             parent_id = ctxt[1]
             self.subcontext = ctxt[2]
         elif isinstance(parent_realm, basestring) and \
                 parent_id is not None and self.id is None:
             # find form(s), if parent descriptors are available
             if subcontext is not None:
                 ctxt = tuple([parent_realm, parent_id, subcontext])
                 self.id = self.forms.get_tracform_meta(ctxt)[0]
         self._get_siblings(parent_realm, parent_id)
         if isinstance(parent_realm, basestring) and \
                 parent_id is not None:
             self.resource = Resource(parent_realm, parent_id
                             ).child('form', self.id, version)
         else:
             raise ResourceNotFound(
                 _("""No data recorded for a TracForms form in
                   %(realm)s:%(parent_id)s
                   """, realm=parent_realm, parent_id=parent_id),
                 subcontext and _("with subcontext %(subcontext)s",
                 subcontext=subcontext) or '')
Ejemplo n.º 16
0
    def remove_ticket_field(self, authname, remove_values):
        for value in remove_values:
            ticket_ids = self._get_ticket_ids(value)
            if not ticket_ids:
                continue

            comment = _('Removed File Biff field value by Trac administrator.')
            fb_methodcaller = methodcaller('remove', value)
            self._update_field(authname, comment, ticket_ids, fb_methodcaller)
Ejemplo n.º 17
0
 def _add_per_ticket_type_option(self, ticket_type):
     self.opt_inherit_fields[ticket_type] = ListOption \
         ('subtickets','type.%s.child_inherits' % ticket_type,
          default='',
          doc = _("""
          Comma-separated list of ticket fields whose values are
          to be copied from a parent ticket into a newly created
          child ticket
          """)
          )
     self.opt_columns[ticket_type] = ListOption \
         ('subtickets', 'type.%s.table_columns' % ticket_type,
          default='status,owner',
          doc = _("""
          Comma-separated list of ticket fields whose values are to be
          shown for each child ticket in the subtickets list
          """)
          )
Ejemplo n.º 18
0
 def _render_fields(self, form_id, state):
     fields = json.loads(state is not None and state or '{}')
     rendered = []
     for name, value in fields.iteritems():
         if value == 'on':
            value = _("checked (checkbox)")
         elif value == '':
            value = _("empty (text field)")
         else:
            value = '\'' + value + '\''
         author, time = self.get_tracform_fieldinfo(form_id, name)
         rendered.append(
             {'name': name, 'value': value,
              'author': tag.span(tag_("by %(author)s", author=author),
                                 class_='author'),
              'time': time is not None and tag.span(
                      format_datetime(time), class_='date') or None})
     return rendered
Ejemplo n.º 19
0
def _render_change(old, new):
    rendered = None
    if old and not new:
        rendered = tag(Markup(_("%(value)s reset to default value",
                                value=tag.em(old))))
    elif new and not old:
        rendered = tag(Markup(_("from default value set to %(value)s",
                                value=tag.em(new))))
    elif old and new:
        if len(old) < 20 and len(new) < 20:
            rendered = tag(Markup(_("changed from %(old)s to %(new)s",
                                    old=tag.em(old), new=tag.em(new))))
        else:
            nbsp = tag.br()
            # TRANSLATOR: same as before, but with additional line breaks
            rendered = tag(Markup(_("changed from %(old)s to %(new)s",
                                    old=tag.em(nbsp, old),
                                    new=tag.em(nbsp, new))))
    return rendered
Ejemplo n.º 20
0
    def validate_ticket(self, req, ticket):
        action = req.args.get('action')
        if action == 'resolve':
            db = self.env.get_db_cnx()
            cursor = db.cursor()
            cursor.execute(
                "SELECT parent, child FROM subtickets WHERE parent=%s",
                (ticket.id, ))

            for parent, child in cursor:
                if Ticket(self.env, child)['status'] != 'closed':
                    yield None, _(
                        'Child ticket #%s has not been closed yet') % child

        elif action == 'reopen':
            ids = set(NUMBERS_RE.findall(ticket['parents'] or ''))
            for id in ids:
                if Ticket(self.env, id)['status'] == 'closed':
                    yield None, _('Parent ticket #%s is closed') % id
Ejemplo n.º 21
0
 def _do_switch(self, env, req, form):
     data = {'_dgettext': dgettext}
     data['page_title'] = get_resource_description(env, form.resource,
                                                   href=req.href)
     data['title'] = get_resource_shortname(env, form.resource)
     data['siblings'] = []
     for sibling in form.siblings:
         form_id = tag.strong(tag.a(
                       _("Form %(form_id)s", form_id=sibling[0]),
                         href=req.href.form(sibling[0])))
         if sibling[1] == '':
             data['siblings'].append(form_id)
         else:
             # TRANSLATOR: Form list entry for form select page
             data['siblings'].append(tag(Markup(_(
                           "%(form_id)s (subcontext = '%(subcontext)s')",
                           form_id=form_id, subcontext = sibling[1]))))
     add_stylesheet(req, 'tracforms/tracforms.css')
     return 'switch.html', data, None
Ejemplo n.º 22
0
 def _validate_multiple_field(self, req, field_name, biff_values):
     _values = map(lambda x: x[field_name], biff_values)
     biff_fvalues = [__f.strip() for _f in _values for __f in _f.split(',')]
     fvalues = [_f.strip() for _f in req.args.get(field_name).split(',')]
     for fvalue in fvalues:
         if fvalue in biff_fvalues:
             _msg = _("The value '%(fvalue)s' is already configured.",
                      fvalue=fvalue)
             add_warning(req, _msg)
             return False
     return True
Ejemplo n.º 23
0
 def build_form(self, text):
     if not self.subform:
         form_class = self.form_class
         form_cssid = self.form_cssid or self.subcontext
         form_name = self.form_name or self.subcontext
         dest = self.formatter.req.href('/form/update')
         yield ('<FORM class="printableform" ' +
                 'method="POST" action=%r' % str(dest) +
                 (form_cssid is not None 
                     and ' id="%s"' % form_cssid
                     or '') +
                 (form_name is not None 
                     and ' name="%s"' % form_name
                     or '') +
                 (form_class is not None 
                     and ' class="%s"' % form_class
                     or '') +
                 '>')
         yield text
         if self.allow_submit:
             # TRANSLATOR: Default submit button label
             submit_label = self.submit_label or _("Update Form")
             yield '<INPUT class="buttons" type="submit"'
             if not self.macro.save_tracform_allowed(self.context[0],
                                                     self.context[1]):
                 yield ' disabled="disabled"'
             if self.submit_name:
                 yield ' name=%r' % str(self.submit_name)
             yield ' value=%r' % xml_escape(submit_label)
             yield '>'
         if self.keep_history:
             yield '<INPUT type="hidden"'
             yield ' name="__keep_history__" value="yes">'
         if self.track_fields:
             yield '<INPUT type="hidden"'
             yield ' name="__track_fields__" value="yes">'
         if self.form_updated_on is not None:
             yield '<INPUT type="hidden" name="__basever__"'
             yield ' value="' + str(self.form_updated_on) + '">'
         context = json.dumps(
             self.context, separators=(',', ':'))
         yield '<INPUT type="hidden" ' + \
             'name="__context__" value=%r>' % context
         backpath = self.formatter.req.href(self.formatter.req.path_info)
         yield '<INPUT type="hidden" ' \
                 'name="__backpath__" value=%s>' % str(backpath)
         form_token = self.formatter.req.form_token
         yield '<INPUT type="hidden" ' \
                 'name="__FORM_TOKEN" value=%r>' % str(form_token)
         yield '</FORM>'
     else:
         yield text
Ejemplo n.º 24
0
 def get(self, search, default=None, singleton=True, all=False):
     values = tuple(dict.__getitem__(self, key)
                     for key in sorted(self.keyset(search, all)))
     if singleton:
         if not values:
             return default
         elif len(values) == 1:
             return values[0]
         else:
             raise ValueError(
                 _("Too many results for singleton %r" % key))
     else:
         return values
Ejemplo n.º 25
0
 def get(self, search, default=None, singleton=True, all=False):
     values = tuple(
         dict.__getitem__(self, key)
         for key in sorted(self.keyset(search, all)))
     if singleton:
         if not values:
             return default
         elif len(values) == 1:
             return values[0]
         else:
             raise ValueError(_("Too many results for singleton %r" % key))
     else:
         return values
Ejemplo n.º 26
0
 def _render_fields(self, req, form_id, state):
     fields = json.loads(state is not None and state or '{}')
     rendered = []
     for name, value in fields.iteritems():
         if value == 'on':
             value = _("checked (checkbox)")
         elif value == '':
             value = _("empty (text field)")
         elif isinstance(value, str):
             value = "'".join(['', value, ''])
         else:
             # Still try to display something useful instead of corrupting
             #   parent page beyond hope of recovery through the web_ui.
             value = "'".join(['', repr(value), ''])
         author, time = self.get_tracform_fieldinfo(form_id, name)
         author = format_author(self.env, req, author, 'value')
         rendered.append(
             {'name': name, 'value': value,
              'author': tag.span(tag(_("by %(author)s", author=author)),
                                 class_='author'),
              'time': time})
     return rendered
Ejemplo n.º 27
0
 def _do_switch(self, env, req, form):
     data = {'_dgettext': dgettext}
     data['page_title'] = get_resource_description(env,
                                                   form.resource,
                                                   href=req.href)
     data['title'] = get_resource_shortname(env, form.resource)
     data['siblings'] = []
     for sibling in form.siblings:
         form_id = tag.strong(
             tag.a(_("Form %(form_id)s", form_id=sibling[0]),
                   href=req.href.form(sibling[0])))
         if sibling[1] == '':
             data['siblings'].append(form_id)
         else:
             # TRANSLATOR: Form list entry for form select page
             data['siblings'].append(
                 tag(
                     Markup(
                         _("%(form_id)s (subcontext = '%(subcontext)s')",
                           form_id=form_id,
                           subcontext=sibling[1]))))
     add_stylesheet(req, 'tracforms/tracforms.css')
     return 'switch.html', data, None
Ejemplo n.º 28
0
 def build_form(self, text):
     if not self.subform:
         form_class = self.form_class
         form_cssid = self.form_cssid or self.subcontext
         form_name = self.form_name or self.subcontext
         dest = self.formatter.req.href('/form/update')
         yield ('<FORM class="printableform" ' +
                 'method="POST" action=%r' % str(dest) +
                 (form_cssid is not None 
                     and ' id="%s"' % form_cssid
                     or '') +
                 (form_name is not None 
                     and ' name="%s"' % form_name
                     or '') +
                 (form_class is not None 
                     and ' class="%s"' % form_class
                     or '') +
                 '>')
         yield text
         if self.allow_submit:
             # TRANSLATOR: Default submit button label
             submit_label = self.submit_label or _("Update Form")
             yield '<INPUT class="buttons" type="submit"'
             if self.submit_name:
                 yield ' name=%r' % str(self.submit_name)
             yield ' value=%r' % xml_escape(submit_label)
             yield '>'
         if self.keep_history:
             yield '<INPUT type="hidden"'
             yield ' name="__keep_history__" value="yes">'
         if self.track_fields:
             yield '<INPUT type="hidden"'
             yield ' name="__track_fields__" value="yes">'
         if self.form_updated_on is not None:
             yield '<INPUT type="hidden" name="__basever__"'
             yield ' value="' + str(self.form_updated_on) + '">'
         context = json.dumps(
             self.context, separators=(',', ':'))
         yield '<INPUT type="hidden" ' + \
             'name="__context__" value=%r>' % context
         backpath = self.formatter.req.href(self.formatter.req.path_info)
         yield '<INPUT type="hidden" ' \
                 'name="__backpath__" value=%s>' % str(backpath)
         form_token = self.formatter.req.form_token
         yield '<INPUT type="hidden" ' \
                 'name="__FORM_TOKEN" value=%r>' % str(form_token)
         yield '</FORM>'
     else:
         yield text
Ejemplo n.º 29
0
 def create_node(tkt):
     node = g.get_node(tkt.id)
     summary = q(tkt['summary'])
     if label_summary:
         node['label'] = u'#%s %s' % (tkt.id, summary)
     else:
         node['label'] = u'#%s'%tkt.id
     if tkt['status'] == 'closed':
         color = tkt['resolution'] in bc_resolutions and self.bad_closed_color or self.closed_color
     else:
         color = self.opened_color
     node['fillcolor'] = color
     node['URL'] = req.href.ticket(tkt.id)
     node['alt'] = _('Ticket #%(id)s', id=tkt.id)
     node['tooltip'] = summary.replace('\\n', ' &#10;')
     return node
Ejemplo n.º 30
0
    def render_admin_panel(self, req, cat, page, biff_key):
        req.perm.require('TICKET_ADMIN')
        biff_config = ChangefileBiffConfig(self.env, self.config)
        template = 'changefilebiff_admin.html'

        if biff_key:
            # in detail view
            if req.method == 'POST':
                if req.args.get('save') and \
                   self._validate_update(req, biff_key, biff_config):
                    biff_config.update(req.authname, req.args, biff_key)
                    self._add_notice_saved(req)
                    req.redirect(req.href.admin(cat, page))
                elif req.args.get('cancel'):
                    req.redirect(req.href.admin(cat, page))

            biff = biff_config.biff[biff_key]
            return template, {'view': 'detail', 'biff': biff}

        # in list view
        if req.method == 'POST':

            if req.args.get('add') and self._validate_add(req, biff_config):
                biff_config.add(req.args)
                self._add_notice_saved(req)
                req.redirect(req.href.admin(cat, page))

            elif req.args.get('remove'):
                select_keys = req.args.get('sel')
                if select_keys:
                    if not isinstance(select_keys, list):
                        select_keys = [select_keys]

                    biff_config.remove(req.authname, select_keys)
                    self._add_notice_removed(req)
                    req.redirect(req.href.admin(cat, page))
                else:
                    add_warning(req, _('No Biff configuration selected.'))

        biff_values = biff_config.biff.values()
        return template, {
            'view': 'list',
            'biff': biff_config.get_i18n_message_catalog(),
            'biff_values': biff_values
        }
Ejemplo n.º 31
0
    def execute(self):
        formatter = self.formatter
        args = self.args
        name = self.name

        # Look in the formatter req object for evidence we are executing.
        self.subform = getattr(formatter.req, type(self).__name__, False)
        if not self.subform:
            setattr(formatter.req, type(self).__name__, True)
        self.env = dict(getattr(formatter.req, 'tracform_env', ()))

        # Setup preliminary context
        self.page = formatter.req.path_info
        if self.page == '/wiki' or self.page == '/wiki/':
            self.page = '/wiki/WikiStart'
        realm, resource_id = resource_from_page(formatter.env, self.page)

        # Remove leading comments and process commands.
        textlines = []
        errors = []
        srciter = iter(args.split('\n'))
        for line in srciter:
            if line[:1] == '#':
                # Comment or operation.
                line = line.strip()[1:]
                if line[:1] == '!':
                    # It's a command, parse the arguments...
                    kw = {}
                    args = list(self.getargs(line[1:], kw))
                    if len(args):
                        cmd = args.pop(0)
                        fn = getattr(self, 'cmd_' + cmd.lower(), None)
                        if fn is None:
                            errors.append(
                                _("ERROR: No TracForms command '%s'" % cmd))
                        else:
                            try:
                                fn(*args, **kw)
                            except FormError, e:
                                errors.append(str(e))
                            except Exception, e:
                                errors.append(traceback.format_exc())
Ejemplo n.º 32
0
    def execute(self):
        formatter = self.formatter
        args = self.args
        name = self.name

        # Look in the formatter req object for evidence we are executing.
        self.subform = getattr(formatter.req, type(self).__name__, False)
        if not self.subform:
            setattr(formatter.req, type(self).__name__, True)
        self.env = dict(getattr(formatter.req, 'tracform_env', ()))

        # Setup preliminary context
        self.page = formatter.req.path_info
        if self.page == '/wiki' or self.page == '/wiki/':
            self.page = '/wiki/WikiStart'
        realm, resource_id = resource_from_page(formatter.env, self.page)

        # Remove leading comments and process commands.
        textlines = []
        errors = []
        srciter = iter(args.split('\n'))
        for line in srciter:
            if line[:1] == '#':
                # Comment or operation.
                line = line.strip()[1:]
                if line[:1] == '!':
                    # It's a command, parse the arguments...
                    kw = {}
                    args = list(self.getargs(line[1:], kw))
                    if len(args):
                        cmd = args.pop(0)
                        fn = getattr(self, 'cmd_' + cmd.lower(), None)
                        if fn is None:
                            errors.append(
                                _("ERROR: No TracForms command '%s'" % cmd))
                        else:
                            try:
                                fn(*args, **kw)
                            except FormError, e:
                                errors.append(str(e))
                            except Exception, e:
                                errors.append(traceback.format_exc())
Ejemplo n.º 33
0
 def get_tracform_ids(self, src, cursor=None):
     """Returns all child forms of resource specified by parent realm and
     parent id as a list of tuples (form_id and corresponding subcontext).
     """
     cursor = self.get_cursor(cursor)
     sql = """
         SELECT  id,
                 subcontext
         FROM    forms
         WHERE   realm = %s
             AND resource_id = %s
         """
     ids = []
     results = cursor(sql, *src)
     if results is not None:
         for form_id, subcontext in results:
             ids.append(tuple([int(form_id), subcontext]))
     else:
         raise ResourceNotFound(
                 _("""No data recorded for a TracForms form in
                   %(realm)s:%(parent_id)s
                   """, realm=realm, parent_id=resource_id))
     return ids
Ejemplo n.º 34
0
    def filter_stream(self, req, method, filename, stream, data):
        if req.path_info.startswith('/ticket/'):
            div = None
            if 'ticket' in data:
                # get parents data
                ticket = data['ticket']
                # title
                div = tag.div(class_='description')
                if ticket['status'] != 'closed':
                    link = tag.a(_('add'),
                        href=req.href.newticket(parents=ticket.id),
                        title=_('Create new child ticket'))
                    link = tag.span('(', link, ')', class_='addsubticket')
                else:
                    link = None
                div.append(tag.h3(_('Subtickets '), link))

            if 'subtickets' in data:
                # table
                tbody = tag.tbody()
                div.append(tag.table(tbody, class_='subtickets'))

                # tickets
                def _func(children, depth=0):
                    def _sort(children):
                        for sort in reversed(literal_eval(self.sort_children)):
                            transform_key = lambda x: x
                            if isinstance(sort, str):
                                sort_by = sort
                            else:
                                assert(isinstance(sort, list))
                                assert(isinstance(sort[0], str))
                                sort_by = sort[0]
                                if isinstance(sort[1], str):
                                    if sort[1] == "int":
                                        transform_key = int
                                    else:
                                        assert(sort[1] == "float")
                                        transform_key = float
                                else:
                                    assert(isinstance(sort[1], list))
                                    lookup_dict = {v: k for (k, v) in enumerate(sort[1])}
                                    def _lookup(value):
                                        try:
                                            return lookup_dict[value]
                                        except KeyError:
                                            return len(lookup_dict)
                                    transform_key = _lookup

                            if sort_by == 'id':
                                children = sorted(children,
                                                  key=lambda x: transform_key(Ticket(self.env, int(x)).id))
                            else:
                                children = sorted(children,
                                                  key=lambda x: transform_key(Ticket(self.env, int(x))[sort_by]))

                        return children

                    for id in _sort(children):
                        ticket = Ticket(self.env, id)

                        properties_to_show = []
                        # 1st column
                        attrs = {'href': req.href.ticket(id)}
                        if ticket['status'] == 'closed':
                            attrs['class_'] = 'closed'
                        link = tag.a('#%s' % id, **attrs)
                        properties_to_show.append(tag.td(link, ': %s' % ticket['summary'],
                                                         style='padding-left: %dpx;' % (depth * 15)))

                        for property in literal_eval(self.show_fields):
                            properties_to_show.append(tag.td(ticket[property]))

                        tbody.append(apply(tag.tr, properties_to_show))
                        _func(children[id], depth + 1)
                        

                _func(data['subtickets'])

            if div:
                add_stylesheet(req, 'subtickets/css/subtickets.css')
                stream |= Transformer('.//div[@id="ticket"]').append(div)

            div_accumulations = None
            accumulations = literal_eval(self.show_accumulations)
            if 'subtickets' in data and accumulations:
                def _accumulate(children, field, method):
                    assert(method == 'sum')
                    result = 0
                    for id in children:
                        ticket = Ticket(self.env, id)
                        try:
                            result += float(ticket[field])
                        except ValueError:
                            pass
                        result += _accumulate(children[id], field, method)
                    return result

                div_accumulations = tag.div(class_='description')
                
                tbody = tag.tbody()
                div_accumulations(tag.table(tbody, class_='properties'))

                for accumulation in accumulations:
                    tbody.append(tag.tr(tag.td(accumulation[1]), 
                                        tag.td(_accumulate(data['subtickets'],
                                                           accumulation[0],
                                                           accumulation[2]))))

            if div_accumulations:
                stream |= Transformer('.//div[@id="ticket"]').append(div_accumulations)

        return stream
Ejemplo n.º 35
0
    def save_tracform(self,
                      src,
                      state,
                      author,
                      base_version=None,
                      keep_history=False,
                      track_fields=False,
                      cursor=None):
        cursor = self.get_cursor(cursor)
        (form_id, realm, resource_id, subcontext, last_updater,
         last_updated_on, form_keep_history,
         form_track_fields) = self.get_tracform_meta(src)

        if form_keep_history is not None:
            keep_history = form_keep_history
        old_state = self.get_tracform_state(src)
        if form_track_fields is not None:
            track_fields = form_track_fields

        if base_version is not None:
            base_version = int(base_version or 0)

        if ((base_version is None and last_updated_on is None)
                or (base_version == last_updated_on)):
            if state != old_state:
                updated_on = int(time.time())
                if form_id is None:
                    form_id = cursor("""
                        INSERT INTO forms
                            (realm, resource_id, subcontext,
                            state, author, time)
                            VALUES (%s, %s, %s, %s, %s, %s)
                        """, realm, resource_id, subcontext,
                        state, author, updated_on) \
                        .last_id(cursor, 'forms', 'id')
                else:
                    cursor(
                        """
                        UPDATE  forms
                        SET     state = %s,
                                author = %s,
                                time = %s
                        WHERE   id = %s
                        """, state, author, updated_on, form_id)
                    if keep_history:
                        cursor(
                            """
                            INSERT INTO forms_history
                                    (id, time, author, old_state)
                                    VALUES (%s, %s, %s, %s)
                            """, form_id, last_updated_on, last_updater,
                            old_state)
                if track_fields:
                    # Break down old version and new version.
                    old_fields = json.loads(old_state or '{}')
                    new_fields = json.loads(state or '{}')
                    updated_fields = []
                    for field, old_value in old_fields.iteritems():
                        if new_fields.get(field) != old_value:
                            updated_fields.append(field)
                    for field in new_fields:
                        if old_fields.get(field) is None:
                            updated_fields.append(field)
                    self.log.debug('UPDATED: ' + str(updated_fields))
                    for field in updated_fields:
                        if cursor(
                                """
                            SELECT  COUNT(*)
                            FROM    forms_fields
                            WHERE   id = %s
                                AND field = %s""", form_id, field).value:

                            cursor(
                                """
                                UPDATE  forms_fields
                                    SET author = %s,
                                        time = %s
                                WHERE   id = %s
                                    AND field = %s
                                """, author, updated_on, form_id, field)
                        else:
                            cursor(
                                """
                                INSERT INTO forms_fields
                                        (id, field, author, time)
                                VALUES  (%s, %s, %s, %s)
                                """, form_id, field, author, updated_on)
            else:
                updated_on = last_updated_on
                author = last_updater
            return ((form_id, realm, resource_id, subcontext, state, author,
                     updated_on), (form_id, realm, resource_id, subcontext,
                                   old_state, last_updater, last_updated_on))
        else:
            raise ValueError(_("Conflict"))
Ejemplo n.º 36
0
 def get_admin_panels(self, req):
     if 'TICKET_ADMIN' in req.perm:
         yield ('ticket', _('Ticket System'), 'filebiff', _('File Biff'))
Ejemplo n.º 37
0
class Schema(FancyValidator):
    """
    A schema validates a dictionary of values, applying different
    validators (be key) to the different values.  If
    allow_extra_fields=True, keys without validators will be allowed;
    otherwise they will raise Invalid. If filter_extra_fields is
    set to true, then extra fields are not passed back in the results.

    Validators are associated with keys either with a class syntax, or
    as keyword arguments (class syntax is usually easier).  Something
    like::

        class MySchema(Schema):
            name = Validators.PlainText()
            phone = Validators.PhoneNumber()

    These will not be available as actual instance variables, but will
    be collected in a dictionary.  To remove a validator in a subclass
    that is present in a superclass, set it to None, like::

        class MySubSchema(MySchema):
            name = None

    Note that missing fields are handled at the Schema level.  Missing
    fields can have the 'missing' message set to specify the error
    message, or if that does not exist the *schema* message
    'missingValue' is used.
    """

    # These validators will be applied before this schema:
    pre_validators = []
    # These validators will be applied after this schema:
    chained_validators = []
    # If true, then it is not an error when keys that aren't
    # associated with a validator are present:
    allow_extra_fields = False
    # If true, then keys that aren't associated with a validator
    # are removed:
    filter_extra_fields = False
    # If this is given, then any keys that aren't available but
    # are expected  will be replaced with this value (and then
    # validated!)  This does not override a present .if_missing
    # attribute on validators:
    if_key_missing = NoDefault
    # If true, then missing keys will be missing in the result,
    # if the validator doesn't have if_missing on it already:
    ignore_key_missing = False
    compound = True
    fields = {}
    order = []
    accept_iterator = True

    messages = dict(
        notExpected=_('The input field %(name)s was not expected.'),
        missingValue=_('Missing value'),
        badDictType=_('The input must be dict-like'
                      ' (not a %(type)s: %(value)r)'),
        singleValueExpected=_('Please provide only one value'),
    )

    __mutableattributes__ = ('fields', 'chained_validators', 'pre_validators')

    @staticmethod
    def __classinit__(cls, new_attrs):
        FancyValidator.__classinit__(cls, new_attrs)
        # Don't bother doing anything if this is the most parent
        # Schema class (which is the only class with just
        # FancyValidator as a superclass):
        if cls.__bases__ == (FancyValidator, ):
            return cls
        # Scan through the class variables we've defined *just*
        # for this subclass, looking for validators (both classes
        # and instances):
        for key, value in new_attrs.iteritems():
            if key in ('pre_validators', 'chained_validators'):
                if is_validator(value):
                    msg = "Any validator with the name %s will be ignored." % \
                            (key,)
                    warnings.warn(msg, FERuntimeWarning)
                continue
            if is_validator(value):
                cls.fields[key] = value
                delattr(cls, key)
            # This last case means we're overwriting a validator
            # from a superclass:
            elif key in cls.fields:
                del cls.fields[key]

        for name, value in cls.fields.iteritems():
            cls.add_field(name, value)

    def __initargs__(self, new_attrs):
        for key, value in new_attrs.iteritems():
            if key in ('pre_validators', 'chained_validators'):
                if is_validator(value):
                    msg = "Any validator with the name %s will be ignored." % \
                            (key,)
                    warnings.warn(msg, FERuntimeWarning)
                continue
            if is_validator(value):
                self.fields[key] = value
                delattr(self, key)
            # This last case means we're overwriting a validator
            # from a superclass:
            elif key in self.fields:
                del self.fields[key]

    def assert_dict(self, value, state):
        """
        Helper to assure we have proper input
        """
        if not hasattr(value, 'items'):
            # Not a dict or dict-like object
            raise Invalid(
                self.message('badDictType',
                             state,
                             type=type(value),
                             value=value), value, state)

    def _convert_to_python(self, value_dict, state):
        if not value_dict:
            if self.if_empty is not NoDefault:
                return self.if_empty
            value_dict = {}

        for validator in self.pre_validators:
            value_dict = validator.to_python(value_dict, state)

        self.assert_dict(value_dict, state)

        new = {}
        errors = {}
        unused = self.fields.keys()
        if state is not None:
            previous_key = getattr(state, 'key', None)
            previous_full_dict = getattr(state, 'full_dict', None)
            state.full_dict = value_dict
        try:
            for name, value in value_dict.items():
                try:
                    unused.remove(name)
                except ValueError:
                    if not self.allow_extra_fields:
                        raise Invalid(
                            self.message('notExpected', state,
                                         name=repr(name)), value_dict, state)
                    if not self.filter_extra_fields:
                        new[name] = value
                    continue
                validator = self.fields[name]

                # are iterators (list, tuple, set, etc) allowed?
                if self._value_is_iterator(value) and not getattr(
                        validator, 'accept_iterator', False):
                    errors[name] = Invalid(
                        self.message('singleValueExpected', state), value_dict,
                        state)

                if state is not None:
                    state.key = name
                try:
                    new[name] = validator.to_python(value, state)
                except Invalid, e:
                    errors[name] = e

            for name in unused:
                validator = self.fields[name]
                try:
                    if_missing = validator.if_missing
                except AttributeError:
                    if_missing = NoDefault
                if if_missing is NoDefault:
                    if self.ignore_key_missing:
                        continue
                    if self.if_key_missing is NoDefault:
                        try:
                            message = validator.message('missing', state)
                        except KeyError:
                            message = self.message('missingValue', state)
                        errors[name] = Invalid(message, None, state)
                    else:
                        if state is not None:
                            state.key = name
                        try:
                            new[name] = validator.to_python(
                                self.if_key_missing, state)
                        except Invalid, e:
                            errors[name] = e
                else:
                    new[name] = validator.if_missing

            if state is not None:
                state.key = previous_key
            for validator in self.chained_validators:
                if (not hasattr(validator, 'validate_partial') or not getattr(
                        validator, 'validate_partial_form', False)):
                    continue
                try:
                    validator.validate_partial(value_dict, state)
                except Invalid, e:
                    sub_errors = e.unpack_errors()
                    if not isinstance(sub_errors, dict):
                        # Can't do anything here
                        continue
                    merge_dicts(errors, sub_errors)
Ejemplo n.º 38
0
 def op_when(self, field, format='%m/%d/%Y %H:%M:%S'):
     when = self.macro.get_tracform_fieldinfo(self.context, field)[1]
     return (when is not None and format_datetime(when, format=str(format))
             or _("unknown"))
Ejemplo n.º 39
0
class FormNoOperationError(FormError):
    def __init__(self, name):
        FormError.__init__(self, name)

    message = _("ERROR: No TracForms operation '%r'")
Ejemplo n.º 40
0
    def save_tracform(self, src, state, author,
                        base_version=None, keep_history=False,
                        track_fields=False, db=None):
        (form_id, realm, resource_id, subcontext, last_updater,
            last_updated_on, form_keep_history,
            form_track_fields) = self.get_tracform_meta(src, db=db)

        if form_keep_history is not None:
            keep_history = form_keep_history
        old_state = form_id and self.get_tracform_state(form_id) or '{}'
        if form_track_fields is not None:
            track_fields = form_track_fields

        if base_version is not None:
            base_version = int(base_version or 0)

        if ((base_version is None and last_updated_on is None) or
            (base_version == last_updated_on)):
            if state != old_state:
                updated_on = int(time.time())
                db = self._get_db(db)
                cursor = db.cursor()
                if form_id is None:
                    cursor.execute("""
                        INSERT INTO forms
                            (realm, resource_id, subcontext,
                            state, author, time)
                            VALUES (%s, %s, %s, %s, %s, %s)
                        """, (realm, resource_id, subcontext,
                        state, author, updated_on))
                    form_id = db.get_last_id(cursor, 'forms') 
                else:
                    cursor.execute("""
                        UPDATE  forms
                        SET     state=%s,
                                author=%s,
                                time=%s
                        WHERE   id=%s
                        """, (state, author, updated_on, form_id))
                    if keep_history:
                        cursor.execute("""
                            INSERT INTO forms_history
                                    (id, time, author, old_state)
                                    VALUES (%s, %s, %s, %s)
                            """, (form_id, last_updated_on,
                                last_updater, old_state))
                if track_fields:
                    # Break down old version and new version.
                    old_fields = json.loads(old_state)
                    new_fields = json.loads(state or '{}')
                    updated_fields = []
                    for field, old_value in old_fields.iteritems():
                        if new_fields.get(field) != old_value:
                            updated_fields.append(field)
                    for field in new_fields:
                        if old_fields.get(field) is None:
                            updated_fields.append(field)
                    self.log.debug('UPDATED: ' + str(updated_fields))
                    for field in updated_fields:
                        cursor.execute("""
                            SELECT  COUNT(*)
                            FROM    forms_fields
                            WHERE   id=%s
                                AND field=%s""", (form_id, field))
                        if cursor.fetchone()[0] > 0:
                            cursor.execute("""
                                UPDATE  forms_fields
                                    SET author=%s,
                                        time=%s
                                WHERE   id=%s
                                    AND field=%s
                                """, (author, updated_on, form_id, field))
                        else:
                            cursor.execute("""
                                INSERT INTO forms_fields
                                        (id, field, author, time)
                                VALUES  (%s, %s, %s, %s)
                                """, (form_id, field, author, updated_on))
                db.commit()
            else:
                updated_on = last_updated_on
                author = last_updater
            return ((form_id, realm, resource_id, subcontext, state,
                    author, updated_on),
                    (form_id, realm, resource_id, subcontext, old_state,
                    last_updater, last_updated_on))
        else:
            raise ValueError(_("Conflict"))
Ejemplo n.º 41
0
class FormNoCommandError(FormError):
    def __init__(self, name):
        FormError.__init__(self, name)

    message = _("ERROR: No TracForms command '%r'")
Ejemplo n.º 42
0
 def get_navigation_items(self, req):
     if 'TICKET_VIEW' not in req.perm:
         return
     yield ('mainnav', 'depgraph',
            tag.a(_('Depgraph'), href=req.href.depgraph()))
Ejemplo n.º 43
0
    def process_request(self, req):
        path_info = req.path_info[10:]

        img_format = req.args.get('format')
        m = self.IMAGE_RE.search(path_info)
        is_img = m is not None
        if is_img:
            img_format = m.group(1)
            path_info = path_info[:-(10+len(img_format))]

        is_full_graph = not path_info
        with_clusters   = req.args.getbool('with_clusters', False)

        cur_pid = self.pm.get_current_project(req)

        #list of tickets to generate the depgraph for
        tkt_ids=[]

        if is_full_graph:
            # depgraph for full project
            # cluster by milestone
            self.pm.check_component_enabled(self, pid=cur_pid)
            db = self.env.get_read_db()
            cursor = db.cursor()
            if with_clusters:
                q = '''
                    SELECT milestone, id
                    FROM ticket
                    WHERE project_id=%s
                    ORDER BY milestone, id
                '''
            else:
                q = '''
                    SELECT id
                    FROM ticket
                    WHERE project_id=%s
                    ORDER BY id
                '''
            cursor.execute(q, (cur_pid,))
            rows = cursor.fetchall()
            if with_clusters:
                tkt_ids = rows
            else:
                tkt_ids = [r[0] for r in rows]
        else:
            # degraph for resource
            resource = get_real_resource_from_url(self.env, path_info, req.args)

            # project check
            res_pid = resource.pid
            self.pm.check_component_enabled(self, pid=res_pid)
            if res_pid != cur_pid:
                self.pm.redirect_to_project(req, res_pid)

            is_milestone = isinstance(resource, Milestone)
            #Urls to generate the depgraph for a ticket is /depgraph/ticketnum
            #Urls to generate the depgraph for a milestone is /depgraph/milestone/milestone_name
            if is_milestone:
                #we need to query the list of tickets in the milestone
                milestone = resource
                query=Query(self.env, constraints={'milestone' : [milestone.name]}, max=0, project=milestone.pid)
                tkt_ids=[fields['id'] for fields in query.execute()]
            else:
                #the list is a single ticket
                ticket = resource
                tkt_ids = [ticket.id]

        #the summary argument defines whether we place the ticket id or
        #it's summary in the node's label
        label_summary=0
        if 'summary' in req.args:
            label_summary=int(req.args.get('summary'))

        clustering = is_full_graph and with_clusters
        g = self._build_graph(req, tkt_ids, label_summary=label_summary, with_clusters=clustering)
        if is_img or img_format:
            if img_format == 'text':
                #in case g.__str__ returns unicode, we need to convert it in ascii
                req.send(to_unicode(g).encode('ascii', 'replace'), 'text/plain')
            elif img_format == 'debug':
                import pprint
                req.send(
                    pprint.pformat(
                        [TicketLinks(self.env, tkt_id) for tkt_id in tkt_ids]
                        ),
                    'text/plain')
            elif img_format == 'svg':
                req.send(g.render(self.dot_path, img_format), 'image/svg+xml')
            elif img_format is not None:
                req.send(g.render(self.dot_path, img_format), 'text/plain')

            if self.use_gs:
                ps = g.render(self.dot_path, 'ps2')
                gs = subprocess.Popen([self.gs_path, '-q', '-dTextAlphaBits=4', '-dGraphicsAlphaBits=4', '-sDEVICE=png16m', '-sOutputFile=%stdout%', '-'],
                                      stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                img, err = gs.communicate(ps)
                if err:
                    self.log.debug('MasterTickets: Error from gs: %s', err)
            else:
                img = g.render(self.dot_path)
            req.send(img, 'image/png')
        else:
            data = {
                'graph': g,
                'graph_render': partial(g.render, self.dot_path),
                'use_gs': self.use_gs,
                'full_graph': is_full_graph,
                'img_format': self.default_format,
                'summary': label_summary,
                'with_clusters': with_clusters,
            }

            if is_full_graph:
                rsc_url = None
            else:
                if is_milestone:
                    resource = milestone.resource
                    add_ctxtnav(req, _('Back to Milestone %(name)s', name=milestone.name),
                                get_resource_url(self.env, resource, req.href))
                    data['milestone'] = milestone.name
                else: # ticket
                    data['tkt'] = ticket
                    resource = ticket.resource
                    add_ctxtnav(req, _('Back to Ticket #%(id)s', id=ticket.id),
                                get_resource_url(self.env, resource, req.href))
                rsc_url = get_resource_url(self.env, resource)

            data['img_url'] = req.href.depgraph(rsc_url, 'depgraph.%s' % self.default_format,
                                                summary=g.label_summary, with_clusters=int(with_clusters))

            return 'depgraph.html', data, None
Ejemplo n.º 44
0
 def get_navigation_items(self, req):
     if 'TEAMCALENDAR_VIEW' in req.perm:
         yield ('mainnav', 'teamcalendar',
                tag.a(_('Team Calendar'), href=req.href.teamcalendar()))
Ejemplo n.º 45
0
 def get_search_filters(self, req):
     if 'FORM_VIEW' in req.perm:
         # TRANSLATOR: The realm name used as TracSearch filter label
         yield ('form', _("Forms"))
Ejemplo n.º 46
0
 def op_who(self, field):
     # TRANSLATOR: Default updater name
     who = self.macro.get_tracform_fieldinfo(
             self.form_id is not None and self.form_id or self.context,
             field)[0] or _("unknown")
     return format_author(self.formatter.env, self.formatter.req, who)
Ejemplo n.º 47
0
class FormTooManyValuesError(FormError):
    def __init__(self, name):
        FormError.__init__(self, name)

    message = _("""ERROR: Too many values for TracForms form variable %r
        (maybe the same field is being used multiple times?)""")
Ejemplo n.º 48
0
 def op_when(self, field, format='%m/%d/%Y %H:%M:%S'):
     when = self.macro.get_tracform_fieldinfo(
         self.form_id is not None and self.form_id or \
         self.context, field)[1]
     return (when is not None and format_datetime(
             when, format=str(format)) or _("unknown"))
Ejemplo n.º 49
0
    def filter_stream(self, req, method, filename, stream, data):
        if not req.path_info.startswith('/ticket/'):
            return stream

        div = None
        link = None
        button = None

        if 'ticket' in data:
            # get parents data
            ticket = data['ticket']
            # title
            div = tag.div(class_='description')
            if 'TICKET_CREATE' in req.perm(ticket.resource) \
                    and ticket['status'] != 'closed':
                opt_inherit = self.env.config.getlist(
                    'subtickets', 'type.%(type)s.child_inherits' % ticket)
                if self.opt_add_style == 'link':
                    inh = {f: ticket[f] for f in opt_inherit}
                    link = tag.a(_('add'),
                                 href=req.href.newticket(parents=ticket.id,
                                                         **inh))
                    link = tag.span('(', link, ')', class_='addsubticket')
                else:
                    inh = [
                        tag.input(type='hidden', name=f, value=ticket[f])
                        for f in opt_inherit
                    ]

                    button = tag.form(tag.div(tag.input(
                        type="submit",
                        value=_("Create"),
                        title=_("Create a child ticket")),
                                              inh,
                                              tag.input(type="hidden",
                                                        name="parents",
                                                        value=str(ticket.id)),
                                              class_="inlinebuttons"),
                                      method="get",
                                      action=req.href.newticket())
            div.append(button)
            div.append(tag.h3(_('Subtickets '), link))

        if 'subtickets' in data:
            # table
            tbody = tag.tbody()
            div.append(tag.table(tbody, class_='subtickets'))
            # tickets
            self._create_subtickets_table(req, data['subtickets'], tbody)

        if div:
            add_stylesheet(req, 'subtickets/css/subtickets.css')
            '''
            If rendered in preview mode, DIV we're interested in isn't a child
            but the root and transformation won't succeed.
            According to HTML specification, id's must be unique within a
            document, so it's safe to omit the leading '.' in XPath expression
            to select all matching regardless of hierarchy their in.
            '''
            stream |= Transformer('//div[@id="ticket"]').append(div)

        return stream
Ejemplo n.º 50
0
 def op_who(self, field):
     # TRANSLATOR: Default updater name
     who = self.macro.get_tracform_fieldinfo(self.context,
                                             field)[0] or _("unknown")
     return who
Ejemplo n.º 51
0
class SubTicketsModule(Component):

    implements(IRequestFilter, ITicketManipulator, ITemplateProvider,
               ITemplateStreamFilter)

    # Simple Options

    opt_skip_validation = ListOption('subtickets',
                                     'skip_closure_validation',
                                     default=[],
                                     doc=_("""
         Normally, reopening a child with a `closed` parent will be
         refused and closing a parent with non-`closed` children will also
         be refused. Adding either of `reopen` or `resolve` to this option will
         make Subtickets skip this validation for the respective action.
         Separate by comma if both actions are listed.

         Caveat: This functionality will be made workflow-independent in a
         future release of !SubTicketsPlugin.
         """))

    opt_recursion_depth = IntOption('subtickets',
                                    'recursion_depth',
                                    default=-1,
                                    doc=_("""
         Limit the number of recursive levels when listing subtickets.
         Default is infinity, represented by`-1`. The value zero (0)
         limits the listing to immediate children.
         """))

    opt_add_style = ChoiceOption('subtickets',
                                 'add_style', ['button', 'link'],
                                 doc=_("""
         Choose whether to make `Add` look like a button (default) or a link
         """))

    opt_owner_url = Option('subtickets',
                           'owner_url',
                           doc=_("""
                           Currently undocumented.
                           """))

    # Per-ticket type options -- all initialised in __init__()

    opt_inherit_fields = dict()
    opt_columns = dict()

    def _add_per_ticket_type_option(self, ticket_type):

        self.opt_inherit_fields[ticket_type] = ListOption(
            'subtickets',
            'type.%s.child_inherits' % ticket_type,
            default='',
            doc=_("""Comma-separated list of ticket fields whose values are
            to be copied from a parent ticket into a newly created
            child ticket.
            """))

        self.opt_columns[ticket_type] = ListOption('subtickets',
                                                   'type.%s.table_columns' %
                                                   ticket_type,
                                                   default='status,owner',
                                                   doc=_("""
             Comma-separated list of ticket fields whose values are to be
             shown for each child ticket in the subtickets list
             """))

    def __init__(self):
        # The following initialisations must happen inside init()
        # in order to be able to access self.env
        for tt in TicketType.select(self.env):
            self._add_per_ticket_type_option(tt.name)

    # ITemplateProvider methods

    def get_htdocs_dirs(self):
        from pkg_resources import resource_filename
        return [('subtickets', resource_filename(__name__, 'htdocs'))]

    def get_templates_dirs(self):
        return []

    # IRequestFilter methods

    def pre_process_request(self, req, handler):
        return handler

    def post_process_request(self, req, template, data, content_type):
        path = req.path_info

        if path.startswith('/ticket/') or path.startswith('/newticket'):
            # get parent ticket's data
            if data and 'ticket' in data:
                ticket = data['ticket']
                parents = ticket['parents'] or ''
                ids = set(NUMBERS_RE.findall(parents))

                if len(parents) > 0:
                    self._append_parent_links(req, data, ids)

                children = self.get_children(ticket.id)
                if children:
                    data['subtickets'] = children

        elif path.startswith('/admin/ticket/type') \
                and data \
                and set(['add', 'name']).issubset(data.keys()) \
                and data['add'] == 'Add':
            self._add_per_ticket_type_option(data['name'])

        return template, data, content_type

    def _append_parent_links(self, req, data, ids):
        links = []
        for id in sorted(ids, key=lambda x: int(x)):
            try:
                ticket = Ticket(self.env, id)
                elem = tag.a('#%s' % id,
                             href=req.href.ticket(id),
                             class_='%s ticket' % ticket['status'],
                             title=ticket['summary'])
                if len(links) > 0:
                    links.append(', ')
                links.append(elem)
            except ResourceNotFound:
                pass
        for field in data.get('fields', ''):
            if field.get('name') == 'parents':
                field['rendered'] = tag.span(*links)

    # ITicketManipulator methods

    def prepare_ticket(self, req, ticket, fields, actions):
        pass

    def get_children(self, parent_id, depth=0):
        children = {}

        for parent, child in self.env.db_query(
                """
                SELECT parent, child FROM subtickets WHERE parent=%s
                """, (parent_id, )):
            children[child] = None

        if self.opt_recursion_depth > depth or self.opt_recursion_depth == -1:
            for id in children:
                children[id] = self.get_children(id, depth + 1)

        return children

    def validate_ticket(self, req, ticket):
        action = req.args.get('action')

        if action in self.opt_skip_validation:
            return

        if action == 'resolve':

            for parent, child in self.env.db_query(
                    """
                    SELECT parent, child FROM subtickets WHERE parent=%s
                    """, (ticket.id, )):
                if Ticket(self.env, child)['status'] != 'closed':
                    yield None, _("""Cannot close/resolve because child
                         ticket #%(child)s is still open""",
                                  child=child)

        elif action == 'reopen':
            ids = set(NUMBERS_RE.findall(ticket['parents'] or ''))
            for id in ids:
                if Ticket(self.env, id)['status'] == 'closed':
                    msg = _(
                        "Cannot reopen because parent ticket #%(id)s "
                        "is closed",
                        id=id)
                    yield None, msg

    # ITemplateStreamFilter method

    def _create_subtickets_table(self, req, children, tbody, depth=0):
        """Recursively create list table of subtickets
        """
        if not children:
            return
        for id in sorted(children, key=lambda x: int(x)):
            ticket = Ticket(self.env, id)

            # the row
            r = []
            # Always show ID and summary
            attrs = {'href': req.href.ticket(id)}
            if ticket['status'] == 'closed':
                attrs['class_'] = 'closed'
            link = tag.a('#%s' % id, **attrs)
            summary = tag.td(link,
                             ': %s' % ticket['summary'],
                             style='padding-left: %dpx;' % (depth * 15))
            r.append(summary)

            # Add other columns as configured.
            for column in \
                    self.env.config.getlist('subtickets',
                                            'type.%(type)s.table_columns'
                                            % ticket):
                if column == 'owner':
                    if self.opt_owner_url:
                        href = req.href(self.opt_owner_url % ticket['owner'])
                    else:
                        href = req.href.query(status='!closed',
                                              owner=ticket['owner'])
                    e = tag.td(tag.a(ticket['owner'], href=href))
                elif column == 'milestone':
                    href = req.href.query(status='!closed',
                                          milestone=ticket['milestone'])
                    e = tag.td(tag.a(ticket['milestone'], href=href))
                else:
                    e = tag.td(ticket[column])
                r.append(e)
            tbody.append(tag.tr(*r))

            self._create_subtickets_table(req, children[id], tbody, depth + 1)

    def filter_stream(self, req, method, filename, stream, data):
        if not req.path_info.startswith('/ticket/'):
            return stream

        div = None
        link = None
        button = None

        if 'ticket' in data:
            # get parents data
            ticket = data['ticket']
            # title
            div = tag.div(class_='description')
            if 'TICKET_CREATE' in req.perm(ticket.resource) \
                    and ticket['status'] != 'closed':
                opt_inherit = self.env.config.getlist(
                    'subtickets', 'type.%(type)s.child_inherits' % ticket)
                if self.opt_add_style == 'link':
                    inh = {f: ticket[f] for f in opt_inherit}
                    link = tag.a(_('add'),
                                 href=req.href.newticket(parents=ticket.id,
                                                         **inh))
                    link = tag.span('(', link, ')', class_='addsubticket')
                else:
                    inh = [
                        tag.input(type='hidden', name=f, value=ticket[f])
                        for f in opt_inherit
                    ]

                    button = tag.form(tag.div(tag.input(
                        type="submit",
                        value=_("Create"),
                        title=_("Create a child ticket")),
                                              inh,
                                              tag.input(type="hidden",
                                                        name="parents",
                                                        value=str(ticket.id)),
                                              class_="inlinebuttons"),
                                      method="get",
                                      action=req.href.newticket())
            div.append(button)
            div.append(tag.h3(_('Subtickets '), link))

        if 'subtickets' in data:
            # table
            tbody = tag.tbody()
            div.append(tag.table(tbody, class_='subtickets'))
            # tickets
            self._create_subtickets_table(req, data['subtickets'], tbody)

        if div:
            add_stylesheet(req, 'subtickets/css/subtickets.css')
            '''
            If rendered in preview mode, DIV we're interested in isn't a child
            but the root and transformation won't succeed.
            According to HTML specification, id's must be unique within a
            document, so it's safe to omit the leading '.' in XPath expression
            to select all matching regardless of hierarchy their in.
            '''
            stream |= Transformer('//div[@id="ticket"]').append(div)

        return stream
Ejemplo n.º 52
0
    def process_request(self, req):
        req.perm.require('TEAMCALENDAR_VIEW')
        pid = self.pm.get_current_project(req)
        syllabus_id = req.data['syllabus_id']
        self.pm.check_component_enabled(self, syllabus_id=syllabus_id)

        work_days = [int(d) for d in self.work_days.syllabus(syllabus_id)]
        weeks_prior = self.weeks_prior.syllabus(syllabus_id)
        weeks_after = self.weeks_after.syllabus(syllabus_id)

        data = {}

        from_date = req.args.get('from_date', '')
        to_date   = req.args.get('to_date', '')
        from_date = from_date and parse_date_only(from_date) or self.find_default_start(weeks_prior)
        to_date   = to_date   and parse_date_only(to_date)   or self.find_default_end(weeks_after)

        # Check time interval
        force_default = True
        delta = (to_date - from_date).days
        if delta < 0:
            add_warning(req, _('Negative time interval selected. Using default.'))
        elif delta > self.MAX_INTERVAL:
            add_warning(req, _('Too big time interval selected (%(interval)s). '
                               'Using default.', interval=pretty_timedelta(to_date, from_date)))
        else:
            force_default = False

        # Reset interval to default
        if force_default:
            from_date = self.find_default_start(weeks_prior)
            to_date   = self.find_default_end(weeks_after)

        # Message
        data['message'] = ''

        # Current user
        data['authname'] = authname = req.authname

        # Can we update?

        data['can_update_own']    = can_update_own    = ('TEAMCALENDAR_UPDATE_OWN'    in req.perm)
        data['can_update_others'] = can_update_others = ('TEAMCALENDAR_UPDATE_OTHERS' in req.perm)
        data['can_update']        = can_update_own or can_update_others

        # Store dates
        data['today']     = date.today()
        data['from_date'] = from_date
        data['to_date']   = to_date

        # Get all people
        data['people'] = people = self.pm.get_project_users(pid)

        # Update timetable if required
        if 'update_calendar' in req.args:
            req.perm.require('TEAMCALENDAR_UPDATE_OWN')

            # deliberately override dates: want to show result of update
            from_date = current_date = parse_date_only(req.args.get('orig_from_date', ''))
            to_date   = parse_date_only(req.args.get('orig_to_date', ''))
            tuples = []
            while current_date <= to_date:
                if can_update_others:
                    for person in people:
                        status = Decimal(req.args.get(u'%s.%s' % (current_date.isoformat(), person), False))
                        tuples.append((current_date, person, status,))
                elif can_update_own:
                    status = Decimal(req.args.get(u'%s.%s' % (current_date.isoformat(), authname), False))
                    tuples.append((current_date, authname, status,))
                current_date += timedelta(1)

            self.update_timetable(tuples, pid, from_date, to_date)
            data['message'] = _('Timetable updated.')

        # Get the current timetable
        timetable = self.get_timetable(from_date, to_date, people, pid, work_days)

        data['timetable'] = []
        current_date = from_date
        while current_date <= to_date:
            data['timetable'].append(dict(date=current_date, people=timetable[current_date]))
            current_date += timedelta(1)

        for day in data['timetable']:
            day['strdate'] = to_unicode(day['date'].strftime('%a %d/%m/%Y'))

        add_stylesheet(req, 'common/css/jquery-ui/jquery.ui.core.css')
        add_stylesheet(req, 'common/css/jquery-ui/jquery.ui.datepicker.css')
        add_stylesheet(req, 'common/css/jquery-ui/jquery.ui.theme.css')
        add_script(req, 'common/js/jquery.ui.core.js')
        add_script(req, 'common/js/jquery.ui.widget.js')
        add_script(req, 'common/js/jquery.ui.datepicker.js')
        add_script(req, 'common/js/datepicker.js')

        add_stylesheet(req, 'teamcalendar/css/calendar.css')

        data['_'] = _
        return 'teamcalendar.html', data, None
Ejemplo n.º 53
0
 def op_who(self, field):
     # TRANSLATOR: Default updater name
     who = self.macro.get_tracform_fieldinfo(
             self.context, field)[0] or _("unknown")
     return who