# HG changeset patch # User cmlenz # Date 1187304815 0 # Node ID 8424a8afd1a106744a29cd0fefa93d65ba8f073b # Parent ca0fded18882737f41db483b7f98f5c3a75538ef Started implementing platform editing via admin interface. diff --git a/bitten/admin.py b/bitten/admin.py --- a/bitten/admin.py +++ b/bitten/admin.py @@ -93,27 +93,50 @@ data = {} if config_name: - config = BuildConfig.fetch(self.env, config_name) - platforms = list(TargetPlatform.select(self.env, config=config.name)) - - if req.method == 'POST': - if 'save' in req.args: - self._update_config(req, config) - req.redirect(req.abs_href.admin(cat, page)) + if '/' in config_name: + config_name, platform_id = config_name.split('/', 1) + platform_id = int(platform_id) - data['config'] = { - 'name': config.name, 'label': config.label or config.name, - 'active': config.active, 'path': config.path, - 'min_rev': config.min_rev, 'max_rev': config.max_rev, - 'description': config.description, - 'recipe': config.recipe, - 'platforms': [{ - 'name': platform.name, - 'id': platform.id, - 'href': req.href.admin('bitten', 'configs', config.name, - platform.id) - } for platform in platforms] - } + platform = TargetPlatform.fetch(self.env, platform_id) + + if req.method == 'POST': + if 'cancel' in req.args: + req.redirect(req.abs_href.admin(cat, page, config_name)) + elif self._process_platform(req, platform): + req.redirect(req.abs_href.admin(cat, page, config_name)) + + data['platform'] = { + 'id': platform.id, 'name': platform.name, + 'exists': platform.exists, + 'rules': [ + {'property': propname, 'pattern': pattern} + for propname, pattern in platform.rules + ] or [('', '')] + } + + else: + config = BuildConfig.fetch(self.env, config_name) + platforms = list(TargetPlatform.select(self.env, + config=config.name)) + + if req.method == 'POST': + if 'save' in req.args: + self._update_config(req, config) + req.redirect(req.abs_href.admin(cat, page)) + + data['config'] = { + 'name': config.name, 'label': config.label or config.name, + 'active': config.active, 'path': config.path, + 'min_rev': config.min_rev, 'max_rev': config.max_rev, + 'description': config.description, + 'recipe': config.recipe, + 'platforms': [{ + 'name': platform.name, + 'id': platform.id, + 'href': req.href.admin('bitten', 'configs', config.name, + platform.id) + } for platform in platforms] + } else: if req.method == 'POST': @@ -175,10 +198,12 @@ def _remove_configs(self, req): req.perm.assert_permission('BUILD_DELETE') + sel = req.args.get('sel') if not sel: raise TracError('No configuration selected') sel = isinstance(sel, list) and sel or [sel] + db = self.env.get_db_cnx() for name in sel: config = BuildConfig.fetch(self.env, name, db=db) @@ -226,6 +251,33 @@ config.recipe = recipe_xml config.min_rev = req.args.get('min_rev') config.max_rev = req.args.get('max_rev') - config.label = req.args.get('label', '') + config.label = req.args.get('label', config.name) config.description = req.args.get('description', '') config.update() + + def _process_platform(self, req, platform): + platform.name = req.args.get('name') + + properties = [int(key[9:]) for key in req.args.keys() + if key.startswith('property_')] + properties.sort() + patterns = [int(key[8:]) for key in req.args.keys() + if key.startswith('pattern_')] + patterns.sort() + platform.rules = [(req.args.get('property_%d' % property), + req.args.get('pattern_%d' % pattern)) + for property, pattern in zip(properties, patterns) + if req.args.get('property_%d' % property)] + + add_rules = [int(key[9:]) for key in req.args.keys() + if key.startswith('add_rule_')] + if add_rules: + platform.rules.insert(add_rules[0] + 1, ('', '')) + return False + rm_rules = [int(key[8:]) for key in req.args.keys() + if key.startswith('rm_rule_')] + if rm_rules: + del platform.rules[rm_rules[0]] + return False + + return True diff --git a/bitten/templates/bitten_admin_configs.cs b/bitten/templates/bitten_admin_configs.cs --- a/bitten/templates/bitten_admin_configs.cs +++ b/bitten/templates/bitten_admin_configs.cs @@ -50,6 +50,10 @@ +
+ + +
@@ -57,6 +61,42 @@
+
+
+
+ Rules + + + + + + + +
Property nameMatch pattern
+
+
+
+
+ + + + +
+
+
diff --git a/bitten/tests/admin.py b/bitten/tests/admin.py --- a/bitten/tests/admin.py +++ b/bitten/tests/admin.py @@ -247,9 +247,29 @@ config = BuildConfig.fetch(self.env, name='bar') self.assertEqual('Bar', config.label) + def test_process_add_config_cancel(self): + redirected_to = [] + def redirect(url): + redirected_to.append(url) + raise RequestDone + req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'), + abs_href=Href('http://example.org/'), redirect=redirect, + args={'cancel': '', 'name': 'bar', 'label': 'Bar'}) + + provider = BuildConfigurationsAdminPageProvider(self.env) + try: + provider.process_admin_request(req, 'bitten', 'configs', '') + self.fail('Expected RequestDone') + + except RequestDone: + self.assertEqual('http://example.org/admin/bitten/configs', + redirected_to[0]) + configs = list(BuildConfig.select(self.env, include_inactive=True)) + self.assertEqual(0, len(configs)) + def test_process_add_config_no_name(self): req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'), - args={'add': '', 'name': ''}) + args={'add': ''}) provider = BuildConfigurationsAdminPageProvider(self.env) try: @@ -259,6 +279,19 @@ except TracError, e: self.assertEqual('Missing required field "name"', e.message) + def test_process_add_config_no_name(self): + req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'), + args={'add': '', 'name': 'no spaces allowed'}) + + provider = BuildConfigurationsAdminPageProvider(self.env) + try: + provider.process_admin_request(req, 'bitten', 'configs', '') + self.fail('Expected TracError') + + except TracError, e: + self.assertEqual('The field "name" may only contain letters, ' + 'digits, periods, or dashes.', e.message) + def test_process_add_config_no_perms(self): BuildConfig(self.env, name='foo', label='Foo', path='branches/foo', active=True).insert() @@ -296,6 +329,31 @@ redirected_to[0]) assert not BuildConfig.fetch(self.env, name='bar') + def test_process_remove_config_cancel(self): + BuildConfig(self.env, name='foo', label='Foo', path='branches/foo', + active=True).insert() + BuildConfig(self.env, name='bar', label='Bar', path='branches/bar', + min_rev='123', max_rev='456').insert() + + redirected_to = [] + def redirect(url): + redirected_to.append(url) + raise RequestDone + req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'), + abs_href=Href('http://example.org/'), redirect=redirect, + args={'cancel': '', 'sel': 'bar'}) + + provider = BuildConfigurationsAdminPageProvider(self.env) + try: + provider.process_admin_request(req, 'bitten', 'configs', '') + self.fail('Expected RequestDone') + + except RequestDone: + self.assertEqual('http://example.org/admin/bitten/configs', + redirected_to[0]) + configs = list(BuildConfig.select(self.env, include_inactive=True)) + self.assertEqual(2, len(configs)) + def test_process_remove_config_no_selection(self): BuildConfig(self.env, name='foo', label='Foo', path='branches/foo', active=True).insert() @@ -354,6 +412,49 @@ except TracError, e: self.assertEqual('Missing required field "name"', e.message) + def test_process_update_config_invalid_name(self): + BuildConfig(self.env, name='foo', label='Foo', path='branches/foo', + active=True).insert() + + req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'), + args={'save': '', 'name': 'no spaces allowed'}) + + provider = BuildConfigurationsAdminPageProvider(self.env) + try: + provider.process_admin_request(req, 'bitten', 'configs', 'foo') + self.fail('Expected TracError') + + except TracError, e: + self.assertEqual('The field "name" may only contain letters, ' + 'digits, periods, or dashes.', e.message) + + def test_process_update_config_valid(self): + BuildConfig(self.env, name='foo', label='Foo', path='branches/foo', + active=True).insert() + + redirected_to = [] + def redirect(url): + redirected_to.append(url) + raise RequestDone + req = Mock(method='POST', perm=PermissionCache(self.env, 'joe'), + abs_href=Href('http://example.org/'), redirect=redirect, + authname='joe', args={ + 'save': '', 'name': 'foo', 'label': 'Foobar', + 'description': 'Thanks for all the fish!' + }) + + provider = BuildConfigurationsAdminPageProvider(self.env) + try: + provider.process_admin_request(req, 'bitten', 'configs', 'foo') + self.fail('Expected RequestDone') + + except RequestDone: + self.assertEqual('http://example.org/admin/bitten/configs', + redirected_to[0]) + config = BuildConfig.fetch(self.env, name='foo') + self.assertEqual('Foobar', config.label) + self.assertEqual('Thanks for all the fish!', config.description) + def suite(): suite = unittest.TestSuite()