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 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 test_missing_agilo_links_allow(self): """Tests robustness of config in case the 'allow' parameter is missing""" env = self.teh.get_env() env.config.remove('agilo-links', 'allow') lc = LinksConfiguration(env) # Force initialization lc._initialized = False lc.initialize()
def test_config_reloads_links_configuration_on_change(self): self.assert_contains( 'story', LinksConfiguration( self.env).get_allowed_destination_types('requirement')) self.config.change_option('allow', '', section=AgiloConfig.AGILO_LINKS, save=True) self.assert_not_contains( 'story', LinksConfiguration( self.env).get_allowed_destination_types('requirement'))
def get_tickets_matching(self, t_id, summary): """ Returns a list of dictionaries (id: value, summary: value) matching the summary request and excluding the requesting ticket having id = id. """ try: t_id = int(t_id) # Make sure it is an int :-) keyword = re.compile(summary, re.IGNORECASE) db = self.env.get_db_cnx() from agilo.ticket.model import AgiloTicketModelManager sql = """SELECT id, type, summary FROM ticket WHERE id != $id $allowed AND id NOT IN (SELECT dest FROM %s WHERE src = $id UNION SELECT src FROM %s WHERE dest = $id) ORDER BY summary""" \ % (LINKS_TABLE, LINKS_TABLE) sql_query = string.Template(sql) sql_allowed = "AND ticket.type IN ('%s')" t_type = AgiloTicketModelManager( self.env).get(tkt_id=t_id).get_type() linkconfig = LinksConfiguration(self.env) if linkconfig.is_allowed_source_type(t_type): allowed_types = linkconfig.get_allowed_destination_types( t_type) allowed = sql_allowed % '\', \''.join(allowed_types) else: debug(self, "No Key found for #%s#" % repr(t_type)) allowed = '' sql_query = sql_query.substitute({'id': t_id, 'allowed': allowed}) debug(self, "SQL: %s" % sql_query) cursor = db.cursor() cursor.execute(sql_query) results = [] for row in cursor: if keyword.search(row[2] or ''): results.append({ 'id': row[0], 'type': row[1], 'summary': row[2] }) debug(self, "Search Results: %s" % str(results)) return results except Exception, e: warning(self, e) msg = "[%s]: ERROR: Search module unable to complete query!" % \ self.__class__.__name__ raise TracError(msg)
def get_tickets_matching(self, t_id, summary): """ Returns a list of dictionaries (id: value, summary: value) matching the summary request and excluding the requesting ticket having id = id. """ try: t_id = int(t_id) # Make sure it is an int :-) keyword = re.compile(summary, re.IGNORECASE) db = self.env.get_db_cnx() from agilo.ticket.model import AgiloTicketModelManager sql = """SELECT id, type, summary FROM ticket WHERE id != $id $allowed AND id NOT IN (SELECT dest FROM %s WHERE src = $id UNION SELECT src FROM %s WHERE dest = $id) ORDER BY summary""" \ % (LINKS_TABLE, LINKS_TABLE) sql_query = string.Template(sql) sql_allowed = "AND ticket.type IN ('%s')" t_type = AgiloTicketModelManager(self.env).get(tkt_id=t_id).get_type() linkconfig = LinksConfiguration(self.env) if linkconfig.is_allowed_source_type(t_type): allowed_types = linkconfig.get_allowed_destination_types(t_type) allowed = sql_allowed % '\', \''.join(allowed_types) else: debug(self, "No Key found for #%s#" % repr(t_type)) allowed = '' sql_query = sql_query.substitute({'id' : t_id, 'allowed' : allowed}) debug(self, "SQL: %s" % sql_query) cursor = db.cursor() cursor.execute(sql_query) results = [] for row in cursor: if keyword.search(row[2] or ''): results.append({'id': row[0], 'type': row[1], 'summary': row[2]}) debug(self, "Search Results: %s" % str(results)) return results except Exception, e: warning(self, e) msg = "[%s]: ERROR: Search module unable to complete query!" % \ self.__class__.__name__ raise TracError(msg)
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)
def _check_if_user_can_create_referenced_tickets(self, req, data): if 'ticket' not in data: data['can_create_at_least_one_referenced_type'] = False return ticket = data['ticket'] ticket_type = ticket[Key.TYPE] can_create_at_least_one_referenced_type = False for allowed_type in LinksConfiguration( self.env).get_allowed_destination_types(ticket_type): permission_name = CoreTemplateProvider( self.env).get_permission_name_to_create(allowed_type) if permission_name in req.perm: can_create_at_least_one_referenced_type = True break data[ 'can_create_at_least_one_referenced_type'] = can_create_at_least_one_referenced_type
class LinksConfigurationCachingTest(AgiloTestCase): def setUp(self): self.super() self.links_configuration = LinksConfiguration(self.env) def test_initializes_at_init(self): self.assert_true(self.links_configuration.is_initialized()) 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) def _requirement_links(self): return self.links_configuration.get_allowed_destination_types('requirement') def test_invalidating_the_cache_reloads_allowed_links_configuration(self): AgiloConfig(self.env).change_option('allow', 'requirement-task', section='agilo-links', save=True) self.links_configuration.reinitialize() self.assert_not_contains('story', self._requirement_links()) self.assert_contains('task', self._requirement_links()) def _bug_calculated_properties(self): return AgiloTicketSystem(self.env).get_agilo_properties('bug')[0].keys() def test_invalidating_the_cache_reloads_calculated_properties(self): self.assert_contains('total_remaining_time', self._bug_calculated_properties()) AgiloConfig(self.env).change_option('bug.calculate', '', section='agilo-links', save=True) self.links_configuration.reinitialize() self.assert_not_contains('total_remaining_time', self._bug_calculated_properties())
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 setUp(self): self.super() self.links_configuration = LinksConfiguration(self.env)
def _reset_links_configuration(self): # Reinitialize the link configuration lc = LinksConfiguration(self.teh.env) lc._initialized = False lc.initialize()
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 can_create_at_least_one_referenced_type(ticket_type): for allowed_type in LinksConfiguration(self.env).get_allowed_destination_types(ticket_type): permission_name = CoreTemplateProvider(self.env).get_permission_name_to_create(allowed_type) if permission_name in perm: return True return False