def test_changing_type_restore_correct_fields(self): """Tests that changing a ticket type restores the correct fields for that type""" t = AgiloTicket(self.env, t_type=Type.TASK) self.assert_true(self._matching_properties(t)) # Now change that to a story t[Key.TYPE] = Type.USER_STORY self.assert_true(self._matching_properties(t)) # Now reload config and change to bug ac = AgiloConfig(self.env) ac.reload() t[Key.TYPE] = Type.BUG self.assert_true(self._matching_properties(t)) # Now add a field on the fly and see if it is adapting to it agilo_types = ac.get_section(ac.AGILO_TYPES) ticket_custom = ac.get_section(ac.TICKET_CUSTOM) ticket_custom.change_option('customer', 'text') ticket_custom.change_option('customer.label', 'Customer') fields = agilo_types.get_list(Type.BUG) fields.append('customer') # notice the save to force the reload of the config agilo_types.change_option('story', ','.join(fields), save=True) t[Key.TYPE] = Type.USER_STORY self.assert_true('customer' in t.fields_for_type, \ "No 'customer' in %s" % t.fields_for_type) t['customer'] = 'My Own Customer' self.assert_true(self._matching_properties(t)) t.insert() self.assert_true(self._matching_properties(t)) self.assert_equals('My Own Customer', t['customer'])
def test_do_not_sets_default_logo_if_changed(self): test_src = 'my_logo' agilo_config = AgiloConfig(self.env) header_logo = agilo_config.get_section('header_logo') header_logo.change_option('src', test_src) self.assert_equals(test_src, header_logo.get('src')) set_default_agilo_logo(agilo_config) self.assert_equals(test_src, header_logo.get('src'))
def _reset_properly_query_and_report_modules(env): # Reset the properties of query module and report in the trac.ini, in old # agilo was required to remove the original from trac, now we have to make # sure that they are reset again, cause the patching is done in AgiloConfig. ac = AgiloConfig(env) components = ac.get_section('components') # even if they would be set to enabled, removing them wouldn't change the # behavior components.remove_option('trac.ticket.query.querymodule') components.remove_option('trac.ticket.report.reportmodule') components.save()
def _reset_properly_query_and_report_modules(env): # Reset the properties of query module and report in the trac.ini, in old # agilo was required to remove the original from trac, now we have to make # sure that they are reset again, cause the patching is done in AgiloConfig. ac = AgiloConfig(env) components = ac.get_section("components") # even if they would be set to enabled, removing them wouldn't change the # behavior components.remove_option("trac.ticket.query.querymodule") components.remove_option("trac.ticket.report.reportmodule") components.save()
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)
class AgiloConfigTest(AgiloTestCase): def setUp(self): self.super() self.trac_config = self.env.config self.config = AgiloConfig(self.env) # -------------------------------------------------------------------------- # Agilo-specific configuration def test_days_are_fetched_correctly_from_config(self): """Regression test: Check that AgiloConfig uses the right configuration section and that use_days is really a bool, not a string.""" self.trac_config.set('agilo-general', Key.USE_DAYS, False) self.assert_false(self.config.use_days) self.trac_config.set('agilo-general', Key.USE_DAYS, True) self.config.reload() self.assert_true(self.config.use_days) def test_can_enable_agilo_ui(self): self.config.enable_agilo_ui(save=True) self.assert_true(self.config.is_agilo_ui_enabled) self.config.disable_agilo_ui(save=True) self.assert_none(self.config.get('templates_dir', 'inherit')) self.assert_false(self.config.is_agilo_ui_enabled) def test_can_enable_agilo(self): self.config.enable_agilo() self.assert_true(self.config.is_agilo_enabled) self.config.disable_agilo() self.assert_false(self.config.is_agilo_enabled) def test_can_disable_agilo_ui(self): self.assert_true(self.config.is_agilo_ui_enabled) self.config.disable_agilo_ui(save=True) self.assert_false(self.config.is_agilo_ui_enabled) def _set_template_dir(self, config, dirname): config.change_option('templates_dir', dirname, 'inherit', save=True) def test_configuration_detects_outdated_template_path(self): self.assert_true(self.config.is_agilo_enabled) self.assert_true(self.config.is_agilo_ui_enabled) current_dir = '/usr/share/agilo-0.42-r12345.egg/templates' self._set_template_dir(self.config, '') self.assert_false(self.config.is_agilo_ui_enabled) self.assert_true(self.config._is_template_dir_outdated(current_dir)) self._set_template_dir(self.config, '/my/user/configured/template') self.assert_false(self.config._is_template_dir_outdated(current_dir)) self._set_template_dir(self.config, current_dir.replace('12345', '54321')) self.assert_true(self.config._is_template_dir_outdated(current_dir)) def test_knows_when_filtered_burndown_is_enabled(self): self.assert_false(self.config.is_filtered_burndown_enabled()) self.config.change_option('should_reload_burndown_on_filter_change_when_filtering_by_component', True, section=AgiloConfig.AGILO_GENERAL) self.assert_false(self.config.is_filtered_burndown_enabled()) self.config.change_option('backlog_filter_attribute', 'component', section=AgiloConfig.AGILO_GENERAL) self.assert_true(self.config.is_filtered_burndown_enabled()) # -------------------------------------------------------------------------- # modify low-level configuration def test_can_remove_whole_sections(self): section = self.config.get_section('fnord') section.change_option('foo', 'bar') self.assert_true('fnord' in self.trac_config.sections()) self.assert_equals('bar', section.get('foo')) section.remove() self.assert_not_equals('bar', section.get('foo')) def test_can_remove_sections_without_getting_it_first(self): section = self.config.get_section('fnord') section.change_option('foo', 'bar') self.assert_true('fnord' in self.trac_config.sections()) self.assert_equals('bar', section.get('foo')) self.config.remove(section='fnord') self.assert_false(self.config.get_section('fnord').has_option('foo')) self.assert_not_equals('bar', section.get('foo')) def test_config_knows_if_an_option_is_set(self): self.assert_false(self.config.has_option('foo', section='fnord')) self.config.change_option('foo', 'bar', section='fnord') self.assert_true(self.config.has_option('foo', section='fnord')) def test_config_reloads_on_change(self): self.config.change_option('%s.%s' % (Type.BUG, Key.ALIAS), 'Bugone', section=AgiloConfig.AGILO_TYPES, save=True) self.assert_equals('Bugone', self.config.ALIASES.get(Type.BUG)) 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 test_config_writing_key_with_capitals(self): my_section = self.config.get_section('my-section') my_section.change_option('TestMe', 'This is a test', save=True) # Test that it is stored self.assert_equals('This is a test', self.env.config.get('my-section', 'TestMe')) # Test that is case insensitive self.assert_equals('This is a test', self.env.config.get('my-section', 'testme')) self.assert_equals('This is a test', self.env.config.get('my-section', 'TESTME')) def test_config_is_normalizing(self): my_section = self.config.get_section('my-section') my_section.change_option('TestMe', 'This is a test', save=True) # check that in reality only the lowecased version is saved in the # config file trac.ini options = self.config.get_options('my-section') self.assert_true('testme' in options, "TestMe not found in: %s" % options) self.assert_false('TestMe' in options, "TestMe found in: %s" % options) def test_config_not_updating_case_sensitive(self): my_section = self.config.get_section('my-section') my_section.change_option('TestMe', 'This is a test', save=True) # Test that it is asymmetric # Using set will not set the option as it is case insensitive and is # not stored because testme already exists in the trac.ini my_section.set_option('TESTME', 'This is another test', save=True) self.assert_not_equals('This is another test', self.env.config.get('my-section', 'TESTME')) self.assert_equals('This is a test', self.env.config.get('my-section', 'TESTME')) options = self.config.get_options('my-section') self.assert_true('testme' in options, 'TestMe not found in: %s' % options) self.assert_false('TestMe' in options, 'TestMe found in: %s' % options) def test_config_is_case_insensitive_and_overwrites(self): my_section = self.config.get_section('my-section') my_section.change_option('TestMe', 'This is a test', save=True) # Now change the option and check that also the old key, that is the # same actually changed my_section.change_option('TESTME', 'This is another test', save=True) self.assert_equals('This is another test', self.env.config.get('my-section', 'TESTME')) self.assert_not_equals('This is a test', self.env.config.get('my-section', 'testme')) # Now check what it is stored options = self.config.get_options('my-section') self.assert_true('testme' in options, 'testme not found in: %s' % options) self.assert_false('TESTME' in options, 'TESTME found in: %s' % options) # Test it is in the AgiloWrapper also after reload self.config.reload() self.assert_equals('This is another test', self.config.get('TestMe', 'my-section')) def test_config_stores_none_as_empty_string(self): my_section = self.config.get_section('my-section') my_section.set_option('test', 'This is a test', save=True) self.assert_equals('This is a test', self.env.config.get('my-section', 'test')) # Now change the option and check that also the old key, that is the # same actually changed my_section.change_option('test', None, save=True) self.assert_not_equals('This is a test', self.env.config.get('my-section', 'test')) self.assert_equals('', self.env.config.get('my-section', 'test')) # Check real config self.env.config.set('my-section', 'test', None) self.env.config.save() self.assert_equals('', self.env.config.get('my-section', 'test')) def test_dont_strip_non_string_values(self): self.assert_true(self.config.get('foo', default=True, section='trac')) def test_sets_default_agilo_logo_on_new_install(self): # should be set by the initialization so let's check it agilo_logo_src = 'agilo/images/default_logo.png' self.assert_equals(agilo_logo_src, self.env.config.get('header_logo', 'src')) def test_do_not_sets_default_logo_if_changed(self): test_src = 'my_logo' agilo_config = AgiloConfig(self.env) header_logo = agilo_config.get_section('header_logo') header_logo.change_option('src', test_src) self.assert_equals(test_src, header_logo.get('src')) set_default_agilo_logo(agilo_config) self.assert_equals(test_src, header_logo.get('src')) def test_sets_agilo_favicon_on_new_install(self): agilo_favicon = 'agilo/images/favicon.ico' self.assert_equals(agilo_favicon, self.env.config.get('project', 'icon')) def test_doesnt_overwrite_custom_favicons(self): custom_favicon = 'fnord' project_section = AgiloConfig(self.env).get_section('project') project_section.change_option('icon', custom_favicon) self.assert_equals(custom_favicon, project_section.get('icon')) initialize_config(self.env, {}) project_section = AgiloConfig(self.env).get_section('project') self.assert_equals(custom_favicon, project_section.get('icon'))
class AgiloConfigTest(AgiloTestCase): def setUp(self): self.super() self.trac_config = self.env.config self.config = AgiloConfig(self.env) # -------------------------------------------------------------------------- # Agilo-specific configuration def test_days_are_fetched_correctly_from_config(self): """Regression test: Check that AgiloConfig uses the right configuration section and that use_days is really a bool, not a string.""" self.trac_config.set('agilo-general', Key.USE_DAYS, False) self.assert_false(self.config.use_days) self.trac_config.set('agilo-general', Key.USE_DAYS, True) self.config.reload() self.assert_true(self.config.use_days) def test_can_enable_agilo_ui(self): self.config.enable_agilo_ui(save=True) self.assert_true(self.config.is_agilo_ui_enabled) self.config.disable_agilo_ui(save=True) self.assert_none(self.config.get('templates_dir', 'inherit')) self.assert_false(self.config.is_agilo_ui_enabled) def test_can_enable_agilo(self): self.config.enable_agilo() self.assert_true(self.config.is_agilo_enabled) self.config.disable_agilo() self.assert_false(self.config.is_agilo_enabled) def test_can_disable_agilo_ui(self): self.assert_true(self.config.is_agilo_ui_enabled) self.config.disable_agilo_ui(save=True) self.assert_false(self.config.is_agilo_ui_enabled) def _set_template_dir(self, config, dirname): config.change_option('templates_dir', dirname, 'inherit', save=True) def test_configuration_detects_outdated_template_path(self): self.assert_true(self.config.is_agilo_enabled) self.assert_true(self.config.is_agilo_ui_enabled) current_dir = '/usr/share/agilo-0.42-r12345.egg/templates' self._set_template_dir(self.config, '') self.assert_false(self.config.is_agilo_ui_enabled) self.assert_true(self.config._is_template_dir_outdated(current_dir)) self._set_template_dir(self.config, '/my/user/configured/template') self.assert_false(self.config._is_template_dir_outdated(current_dir)) self._set_template_dir(self.config, current_dir.replace('12345', '54321')) self.assert_true(self.config._is_template_dir_outdated(current_dir)) def test_knows_when_filtered_burndown_is_enabled(self): self.assert_false(self.config.is_filtered_burndown_enabled()) self.config.change_option( 'should_reload_burndown_on_filter_change_when_filtering_by_component', True, section=AgiloConfig.AGILO_GENERAL) self.assert_false(self.config.is_filtered_burndown_enabled()) self.config.change_option('backlog_filter_attribute', 'component', section=AgiloConfig.AGILO_GENERAL) self.assert_true(self.config.is_filtered_burndown_enabled()) # -------------------------------------------------------------------------- # modify low-level configuration def test_can_remove_whole_sections(self): section = self.config.get_section('fnord') section.change_option('foo', 'bar') self.assert_true('fnord' in self.trac_config.sections()) self.assert_equals('bar', section.get('foo')) section.remove() self.assert_not_equals('bar', section.get('foo')) def test_can_remove_sections_without_getting_it_first(self): section = self.config.get_section('fnord') section.change_option('foo', 'bar') self.assert_true('fnord' in self.trac_config.sections()) self.assert_equals('bar', section.get('foo')) self.config.remove(section='fnord') self.assert_false(self.config.get_section('fnord').has_option('foo')) self.assert_not_equals('bar', section.get('foo')) def test_config_knows_if_an_option_is_set(self): self.assert_false(self.config.has_option('foo', section='fnord')) self.config.change_option('foo', 'bar', section='fnord') self.assert_true(self.config.has_option('foo', section='fnord')) def test_config_reloads_on_change(self): self.config.change_option('%s.%s' % (Type.BUG, Key.ALIAS), 'Bugone', section=AgiloConfig.AGILO_TYPES, save=True) self.assert_equals('Bugone', self.config.ALIASES.get(Type.BUG)) 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 test_config_writing_key_with_capitals(self): my_section = self.config.get_section('my-section') my_section.change_option('TestMe', 'This is a test', save=True) # Test that it is stored self.assert_equals('This is a test', self.env.config.get('my-section', 'TestMe')) # Test that is case insensitive self.assert_equals('This is a test', self.env.config.get('my-section', 'testme')) self.assert_equals('This is a test', self.env.config.get('my-section', 'TESTME')) def test_config_is_normalizing(self): my_section = self.config.get_section('my-section') my_section.change_option('TestMe', 'This is a test', save=True) # check that in reality only the lowecased version is saved in the # config file trac.ini options = self.config.get_options('my-section') self.assert_true('testme' in options, "TestMe not found in: %s" % options) self.assert_false('TestMe' in options, "TestMe found in: %s" % options) def test_config_not_updating_case_sensitive(self): my_section = self.config.get_section('my-section') my_section.change_option('TestMe', 'This is a test', save=True) # Test that it is asymmetric # Using set will not set the option as it is case insensitive and is # not stored because testme already exists in the trac.ini my_section.set_option('TESTME', 'This is another test', save=True) self.assert_not_equals('This is another test', self.env.config.get('my-section', 'TESTME')) self.assert_equals('This is a test', self.env.config.get('my-section', 'TESTME')) options = self.config.get_options('my-section') self.assert_true('testme' in options, 'TestMe not found in: %s' % options) self.assert_false('TestMe' in options, 'TestMe found in: %s' % options) def test_config_is_case_insensitive_and_overwrites(self): my_section = self.config.get_section('my-section') my_section.change_option('TestMe', 'This is a test', save=True) # Now change the option and check that also the old key, that is the # same actually changed my_section.change_option('TESTME', 'This is another test', save=True) self.assert_equals('This is another test', self.env.config.get('my-section', 'TESTME')) self.assert_not_equals('This is a test', self.env.config.get('my-section', 'testme')) # Now check what it is stored options = self.config.get_options('my-section') self.assert_true('testme' in options, 'testme not found in: %s' % options) self.assert_false('TESTME' in options, 'TESTME found in: %s' % options) # Test it is in the AgiloWrapper also after reload self.config.reload() self.assert_equals('This is another test', self.config.get('TestMe', 'my-section')) def test_config_stores_none_as_empty_string(self): my_section = self.config.get_section('my-section') my_section.set_option('test', 'This is a test', save=True) self.assert_equals('This is a test', self.env.config.get('my-section', 'test')) # Now change the option and check that also the old key, that is the # same actually changed my_section.change_option('test', None, save=True) self.assert_not_equals('This is a test', self.env.config.get('my-section', 'test')) self.assert_equals('', self.env.config.get('my-section', 'test')) # Check real config self.env.config.set('my-section', 'test', None) self.env.config.save() self.assert_equals('', self.env.config.get('my-section', 'test')) def test_dont_strip_non_string_values(self): self.assert_true(self.config.get('foo', default=True, section='trac')) def test_sets_default_agilo_logo_on_new_install(self): # should be set by the initialization so let's check it agilo_logo_src = 'agilo/images/default_logo.png' self.assert_equals(agilo_logo_src, self.env.config.get('header_logo', 'src')) def test_do_not_sets_default_logo_if_changed(self): test_src = 'my_logo' agilo_config = AgiloConfig(self.env) header_logo = agilo_config.get_section('header_logo') header_logo.change_option('src', test_src) self.assert_equals(test_src, header_logo.get('src')) set_default_agilo_logo(agilo_config) self.assert_equals(test_src, header_logo.get('src')) def test_sets_agilo_favicon_on_new_install(self): agilo_favicon = 'agilo/images/favicon.ico' self.assert_equals(agilo_favicon, self.env.config.get('project', 'icon')) def test_doesnt_overwrite_custom_favicons(self): custom_favicon = 'fnord' project_section = AgiloConfig(self.env).get_section('project') project_section.change_option('icon', custom_favicon) self.assert_equals(custom_favicon, project_section.get('icon')) initialize_config(self.env, {}) project_section = AgiloConfig(self.env).get_section('project') self.assert_equals(custom_favicon, project_section.get('icon'))
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))
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))