Beispiel #1
0
 def _create_custom_ticket_type(self, type_name, field_names):
     custom_type = TicketType(self.env)
     custom_type.name = type_name
     custom_type.insert()
     
     config = AgiloConfig(self.env)
     config.change_option(type_name, field_names, section=AgiloConfig.AGILO_TYPES)
     config.reload()
     self.assert_true(type_name in config.get_available_types())
Beispiel #2
0
    def _create_custom_ticket_type(self, type_name, field_names):
        custom_type = TicketType(self.env)
        custom_type.name = type_name
        custom_type.insert()

        config = AgiloConfig(self.env)
        config.change_option(type_name,
                             field_names,
                             section=AgiloConfig.AGILO_TYPES)
        config.reload()
        self.assert_true(type_name in config.get_available_types())
    def test_handles_types_with_dashes(self):
        from_type = 'with-dashes'
        to_type = 'bug'
        custom_type = TicketType(self.env)
        custom_type.name = from_type
        custom_type.insert()

        config = AgiloConfig(self.env)
        config.change_option(from_type, "", section=AgiloConfig.AGILO_TYPES)
        config.reload()
        self.assert_true(from_type in config.get_available_types())

        section = config.get_section(AgiloConfig.AGILO_LINKS)
        allowed_links = section.get_list('allow')
        allowed_links.append('%s-%s' % (from_type, to_type))
        section.change_option('allow', ', '.join(allowed_links), save=True)
        self.links_configuration = LinksConfiguration(self.env)
        self.assert_equals(self.links_configuration.get_alloweds(from_type)[0].dest_type, to_type)
Beispiel #4
0
class TypesAdminPanel(AgiloAdminPanel):
    """
    Administration panel for different ticket types and their fields.
    Needs to get imported in agilo/admin/__init__.py in order to appear
    on the web interface.
    """

    _type = 'types'
    _label = ('Types', _('Types'))

    def __init__(self):
        self.agilo_config = AgiloConfig(self.env)

    def _get_field_labels(self):
        """Returns a dictionary of fields and their labels."""
        labels = {}
        for f_name, label in self.agilo_config.LABELS.items():
            if f_name not in MANDATORY_FIELDS:
                labels[f_name] = label
        return labels

    def _get_fields(self, type_name=None):
        """
        If type_name is not set, return a dictionary of ticket types 
        and the corresponding fields as a list. If a type_name gets 
        passed, return a list of fields corresponding to this ticket 
        type.
        """
        fields = self.agilo_config.get_available_types(with_fields=True)
        if type_name:
            fields = fields.get(type_name)
        return fields

    def detail_save_view(self, req, cat, page, ticket_type):
        """Save the detail panel view"""
        # The types will be stored in lowercase and the space is not a
        # valid character for the config file key
        ticket_type = normalize_ticket_type(ticket_type)

        alias = req.args.get(Key.ALIAS)
        if alias:
            self.agilo_config.change_option('%s.%s' % (ticket_type, Key.ALIAS),
                                            alias,
                                            section=AgiloConfig.AGILO_TYPES,
                                            save=False)
        # save fields as string or comma-separated list of values
        # FIXME: (AT) after going crazy, it came out that some types are not
        # saved because there is no specific field assigned and the config
        # won't store the property in the trac.ini. So the agilo type will also
        # not be loaded, even if the alias would be set.
        fields = req.args.get(Key.FIELDS, '')
        # We have to save strings not lists
        if isinstance(fields, list):
            fields = ', '.join(fields)
        self.agilo_config.change_option(ticket_type,
                                        fields,
                                        section=AgiloConfig.AGILO_TYPES,
                                        save=False)
        calc = []
        for res, func in zip(req.args.getlist('result'),
                             req.args.getlist('function')):
            if res and func:
                configstring = u'%s=%s' % (res.strip(), func.strip())
                parsed = parse_calculated_field(configstring)
                if parsed == None:
                    msg = u"Wrong format for calculated property '%s'"
                    raise TracError(_(msg) % res)
                calc.append(configstring)
        calc = ','.join(calc)
        if calc:
            self.agilo_config.change_option(
                '%s.%s' % (ticket_type, LinkOption.CALCULATE),
                calc,
                section=AgiloConfig.AGILO_LINKS,
                save=False)
        self.agilo_config.save()

        # on 0.12 we need to reset the ticket fields explicitely as the synchronization
        # is not done with the trac.ini anymore
        if AgiloTicketSystem(self.env).is_trac_012():
            AgiloTicketSystem(self.env).reset_ticket_fields()
        return req.redirect(req.href.admin(cat, page))

    def detail_view(self, req, cat, page, ticket_type):
        # All keys are saved lower-cased, but this is not done
        # automatically for retrieval
        calc_prop = self.agilo_config.get_list(
            '%s.%s' % (ticket_type, LinkOption.CALCULATE),
            section=AgiloConfig.AGILO_LINKS)
        calculated_properties = []
        if len(calc_prop) > 0:
            for definition in calc_prop:
                parts = definition.split('=', 1)
                if len(parts) == 2:
                    property_name, formula = parts
                    calculated_properties.append(
                        (property_name.strip(), formula.strip()))
                else:
                    message = u"Ignoring broken definition for " \
                              "calculated property: %s" % definition
                    warning(self, message)
        data = {
            'calculate': calculated_properties,
            'view': 'detail',
            'type': ticket_type,
            'alias': self.agilo_config.ALIASES.get(ticket_type, ''),
            'type_fields': self._get_fields(ticket_type),
            'labels': self._get_field_labels(),
        }
        return 'agilo_admin_types.html', data

    def list_view(self, req, cat, page):
        data = {
            'view': 'list',
            'fields': self._get_fields(),
            'aliases': self.agilo_config.ALIASES,
            'labels': self._get_field_labels(),
        }
        return 'agilo_admin_types.html', data
Beispiel #5
0
class TypesAdminPanel(AgiloAdminPanel):
    """
    Administration panel for different ticket types and their fields.
    Needs to get imported in agilo/admin/__init__.py in order to appear
    on the web interface.
    """
    
    _type = 'types'
    _label = ('Types', _('Types'))

    def __init__(self):
        self.agilo_config = AgiloConfig(self.env)

    def _get_field_labels(self):
        """Returns a dictionary of fields and their labels."""
        labels = {}
        for f_name, label in self.agilo_config.LABELS.items():
            if f_name not in MANDATORY_FIELDS:
                labels[f_name] = label
        return labels

    def _get_fields(self, type_name=None):
        """
        If type_name is not set, return a dictionary of ticket types 
        and the corresponding fields as a list. If a type_name gets 
        passed, return a list of fields corresponding to this ticket 
        type.
        """
        fields = self.agilo_config.get_available_types(with_fields=True)
        if type_name:
            fields = fields.get(type_name)
        return fields
    
    def detail_save_view(self, req, cat, page, ticket_type):
        """Save the detail panel view"""
        # The types will be stored in lowercase and the space is not a
        # valid character for the config file key
        ticket_type = normalize_ticket_type(ticket_type)
        
        alias = req.args.get(Key.ALIAS)
        if alias:
            self.agilo_config.change_option('%s.%s' % (ticket_type, Key.ALIAS), 
                                            alias, 
                                            section=AgiloConfig.AGILO_TYPES,
                                            save=False)
        # save fields as string or comma-separated list of values
        # FIXME: (AT) after going crazy, it came out that some types are not
        # saved because there is no specific field assigned and the config 
        # won't store the property in the trac.ini. So the agilo type will also
        # not be loaded, even if the alias would be set.
        fields = req.args.get(Key.FIELDS, '') 
        # We have to save strings not lists
        if isinstance(fields, list):
            fields = ', '.join(fields)
        self.agilo_config.change_option(ticket_type, 
                                        fields,
                                        section=AgiloConfig.AGILO_TYPES,
                                        save=False)
        calc = []
        for res, func in zip(req.args.getlist('result'), 
                             req.args.getlist('function')):
            if res and func:
                configstring = u'%s=%s' % (res.strip(), func.strip())
                parsed = parse_calculated_field(configstring)
                if parsed == None:
                    msg = u"Wrong format for calculated property '%s'"
                    raise TracError(_(msg) % res)
                calc.append(configstring)
        calc = ','.join(calc)
        if calc:
            self.agilo_config.change_option('%s.%s' % (ticket_type, 
                                                       LinkOption.CALCULATE), 
                                            calc,
                                            section=AgiloConfig.AGILO_LINKS,
                                            save=False)
        self.agilo_config.save()
        
        # on 0.12 we need to reset the ticket fields explicitely as the synchronization 
        # is not done with the trac.ini anymore
        if AgiloTicketSystem(self.env).is_trac_012():
            AgiloTicketSystem(self.env).reset_ticket_fields()
        return req.redirect(req.href.admin(cat, page))
    
    def detail_view(self, req, cat, page, ticket_type):
        # All keys are saved lower-cased, but this is not done 
        # automatically for retrieval
        calc_prop = self.agilo_config.get_list('%s.%s' % (ticket_type, 
                                                          LinkOption.CALCULATE),
                                               section=AgiloConfig.AGILO_LINKS)
        calculated_properties = []
        if len(calc_prop) > 0:
            for definition in calc_prop:
                parts = definition.split('=', 1)
                if len(parts) == 2:
                    property_name, formula = parts
                    calculated_properties.append((property_name.strip(), 
                                                  formula.strip()))
                else:
                    message = u"Ignoring broken definition for " \
                              "calculated property: %s" % definition
                    warning(self, message)
        data = {
            'calculate' : calculated_properties,
            'view': 'detail',
            'type': ticket_type,
            'alias' : self.agilo_config.ALIASES.get(ticket_type, ''),
            'type_fields' : self._get_fields(ticket_type),
            'labels' : self._get_field_labels(),
        }
        return 'agilo_admin_types.html', data
    
    def list_view(self, req, cat, page):
        data = {
            'view': 'list',
            'fields': self._get_fields(),
            'aliases' : self.agilo_config.ALIASES,
            'labels' : self._get_field_labels(),
        }
        return 'agilo_admin_types.html', data
Beispiel #6
0
class LinksAdminPanel(AgiloAdminPanel):
    """Administration panel for links.
    Needs to get imported in agilo/admin/__init__.py in order to appear
    on the web interface."""

    _type = 'links'
    _label = ('Links', _('Links'))

    def __init__(self):
        self.config = AgiloConfig(self.env)
        self.links = self.config.get_section(AgiloConfig.AGILO_LINKS)
        self.allowed_links = self._get_allowed_links()

    def _get_allowed_links(self):
        """Returns the dictionary containing the allowed links pairs"""
        links_configuration = LinksConfiguration(self.env)
        return dict([(l, list(links_configuration.extract_types(l)))
                     for l in self.links.get_list(LinkOption.ALLOW)])

    def _get_delete_pairs(self):
        """Returns the dictionary containing the cascade delete pairs"""
        links_configuration = LinksConfiguration(self.env)
        return dict([(l, list(links_configuration.extract_types(l)))
                     for l in self.links.get_list(LinkOption.DELETE)])

    def detail_view(self, req, cat, page, link):
        links_configuration = LinksConfiguration(self.env)
        (source, target) = links_configuration.extract_types(link)
        copy_fields = [f.strip() for f in self.links.get('%s.%s.%s' % \
                                                         (source, target, LinkOption.COPY),
                                                         default='').split(',')]
        show_fields = [f.strip() for f in self.links.get('%s.%s.%s' % \
                                                         (source, target, LinkOption.SHOW),
                                                         default='').split(',')]
        ticket_system = AgiloTicketSystem(self.env)
        # dict of name->label for all core and custom fields
        labels = dict([(f['name'], f['label'])
                       for f in ticket_system.get_ticket_fields()])
        cascade_delete = source + '-' + target in self._get_delete_pairs()
        data = {
            'view': 'detail',
            'link': link,
            'source': source,
            'target': target,
            'source_fields': self.config.TYPES[source],
            'target_fields': self.config.TYPES[target],
            'labels': labels,
            'copy_fields': copy_fields,
            'show_fields': show_fields,
            'cascade_delete': cascade_delete
        }
        return 'agilo_admin_links.html', data

    def detail_save_view(self, req, cat, page, link):
        links_configuration = LinksConfiguration(self.env)
        (source, target) = links_configuration.extract_types(link)
        fields = req.args.get('copy_fields', [])
        if type(fields) != type([]):
            fields = [fields]
        # set copy options for this link
        self.links.change_option(
            '%s.%s.%s' % (source, target, LinkOption.COPY), ', '.join(fields))

        fields = req.args.get('show_fields', [])
        if type(fields) != type([]):
            fields = [fields]
        # set show options for this link
        self.links.change_option(
            '%s.%s.%s' % (source, target, LinkOption.SHOW), ', '.join(fields))

        cascade_delete = req.args.get('cascade_delete')
        delete_pairs = self._get_delete_pairs()
        if cascade_delete and source + '-' + target not in delete_pairs:
            delete_pairs[source + '-' + target] = (source, target)
            self.links.change_option(LinkOption.DELETE,
                                     ', '.join(delete_pairs.keys()))

        elif not cascade_delete and source + '-' + target in self._get_delete_pairs(
        ):
            del delete_pairs[source + '-' + target]
            self.links.change_option(LinkOption.DELETE,
                                     ', '.join(delete_pairs.keys()))

        # saved it, redirect back to admin view
        self.links.save()
        req.redirect(req.href.admin(cat, page))

    def list_view(self, req, cat, page):
        data = {
            'view': 'list',
            'allowed_links': self._get_allowed_links(),
            'available_types': self.config.get_available_types(strict=True),
        }
        return 'agilo_admin_links.html', data

    def list_save_view(self, req, cat, page):
        source = req.args.get('source')
        target = req.args.get('target')
        if req.args.get('add') and source and target:
            if (source, target) in self.allowed_links:
                # link already exists, redirect to it
                req.redirect(
                    req.href.admin(cat, page, '%s-%s' % (source, target)))
            # Set, save because there is the redirect
            self.links.change_option(LinkOption.ALLOW, '%s, %s-%s' % \
                                     (self.links.get(LinkOption.ALLOW, default=''),
                                      source, target), save=True)
            return req.redirect(
                req.href.admin(cat, page, '%s-%s' % (source, target)))

        # Remove components
        if req.args.get('remove'):
            sel = req.args.get('sel')
            if not sel:
                raise TracError(_('No link selected'))
            if not isinstance(sel, list):
                sel = [sel]

            # delete selection from allowed links
            for s in sel:
                del self.allowed_links[s]

            # write new value back to config, and save
            self.links.change_option(LinkOption.ALLOW,
                                     ', '.join(self.allowed_links.keys()),
                                     save=True)
        return req.redirect(req.href.admin(cat, page))
Beispiel #7
0
class LinksAdminPanel(AgiloAdminPanel):
    """Administration panel for links.
    Needs to get imported in agilo/admin/__init__.py in order to appear
    on the web interface."""
    
    _type = 'links'
    _label = ('Links', _('Links'))

    def __init__(self):
        self.config = AgiloConfig(self.env)
        self.links = self.config.get_section(AgiloConfig.AGILO_LINKS)
        self.allowed_links = self._get_allowed_links()
    
    def _get_allowed_links(self):
        """Returns the dictionary containing the allowed links pairs"""
        links_configuration = LinksConfiguration(self.env)
        return dict([(l, list(links_configuration.extract_types(l))) for l in self.links.get_list(LinkOption.ALLOW)])

    def _get_delete_pairs(self):
        """Returns the dictionary containing the cascade delete pairs"""
        links_configuration = LinksConfiguration(self.env)
        return dict([(l, list(links_configuration.extract_types(l))) for l in self.links.get_list(LinkOption.DELETE)])

    def detail_view(self, req, cat, page, link):
        links_configuration = LinksConfiguration(self.env)
        (source, target) = links_configuration.extract_types(link)
        copy_fields = [f.strip() for f in self.links.get('%s.%s.%s' % \
                                                         (source, target, LinkOption.COPY), 
                                                         default='').split(',')]
        show_fields = [f.strip() for f in self.links.get('%s.%s.%s' % \
                                                         (source, target, LinkOption.SHOW), 
                                                         default='').split(',')]
        ticket_system = AgiloTicketSystem(self.env)
        # dict of name->label for all core and custom fields
        labels = dict([(f['name'], f['label']) for f in ticket_system.get_ticket_fields()])
        cascade_delete = source+'-'+target in self._get_delete_pairs()
        data = {
            'view': 'detail',
            'link': link,
            'source' : source,
            'target' : target,
            'source_fields' : self.config.TYPES[source],
            'target_fields' : self.config.TYPES[target],
            'labels' : labels,
            'copy_fields' : copy_fields,
            'show_fields' : show_fields,
            'cascade_delete': cascade_delete
        }
        return 'agilo_admin_links.html', data
        
    def detail_save_view(self, req, cat, page, link):
        links_configuration = LinksConfiguration(self.env)
        (source, target) = links_configuration.extract_types(link)
        fields = req.args.get('copy_fields', [])
        if type(fields) != type([]):
            fields = [fields]
        # set copy options for this link
        self.links.change_option('%s.%s.%s' % (source, target, LinkOption.COPY),
                                 ', '.join(fields))
        
        fields = req.args.get('show_fields', [])
        if type(fields) != type([]):
            fields = [fields]
        # set show options for this link
        self.links.change_option('%s.%s.%s' % (source, target, LinkOption.SHOW),
                                 ', '.join(fields))
        
        cascade_delete = req.args.get('cascade_delete')
        delete_pairs = self._get_delete_pairs()
        if cascade_delete and source+'-'+target not in delete_pairs:
            delete_pairs[source+'-'+target] = (source, target)
            self.links.change_option(LinkOption.DELETE, 
                                     ', '.join(delete_pairs.keys()))

        elif not cascade_delete and source+'-'+target in self._get_delete_pairs():
            del delete_pairs[source+'-'+target]
            self.links.change_option(LinkOption.DELETE, 
                                     ', '.join(delete_pairs.keys()))
            
        
        # saved it, redirect back to admin view
        self.links.save()
        req.redirect(req.href.admin(cat, page))
   
    def list_view(self, req, cat, page):
        data = {
            'view': 'list',
            'allowed_links': self._get_allowed_links(),
            'available_types' : self.config.get_available_types(strict=True),
        }
        return 'agilo_admin_links.html', data
        
    def list_save_view(self, req, cat, page):
        source = req.args.get('source')
        target = req.args.get('target')
        if req.args.get('add') and source and target:
            if (source, target) in self.allowed_links:
                # link already exists, redirect to it
                req.redirect(req.href.admin(cat, page, '%s-%s' % (source, target)))
            # Set, save because there is the redirect
            self.links.change_option(LinkOption.ALLOW, '%s, %s-%s' % \
                                     (self.links.get(LinkOption.ALLOW, default=''), 
                                      source, target), save=True)
            return req.redirect(req.href.admin(cat, page, '%s-%s' % (source, target)))

        # Remove components
        if req.args.get('remove'):
            sel = req.args.get('sel')
            if not sel:
                raise TracError(_('No link selected'))
            if not isinstance(sel, list):
                sel = [sel]

            # delete selection from allowed links
            for s in sel:
                del self.allowed_links[s]
                
            # write new value back to config, and save
            self.links.change_option(LinkOption.ALLOW, 
                                     ', '.join(self.allowed_links.keys()),
                                     save=True)
        return req.redirect(req.href.admin(cat, page))