##// END OF EJS Templates
implements #226 repo groups available by path...
marcink -
r1538:27be8f94 beta
parent child Browse files
Show More
@@ -19,10 +19,10 b' def make_map(config):'
19 always_scan=config['debug'])
19 always_scan=config['debug'])
20 rmap.minimization = False
20 rmap.minimization = False
21 rmap.explicit = False
21 rmap.explicit = False
22
22
23 from rhodecode.lib.utils import is_valid_repo
23 from rhodecode.lib.utils import is_valid_repo
24 from rhodecode.lib.utils import is_valid_repos_group
24 from rhodecode.lib.utils import is_valid_repos_group
25
25
26 def check_repo(environ, match_dict):
26 def check_repo(environ, match_dict):
27 """
27 """
28 check for valid repository for proper 404 handling
28 check for valid repository for proper 404 handling
@@ -30,7 +30,7 b' def make_map(config):'
30 :param environ:
30 :param environ:
31 :param match_dict:
31 :param match_dict:
32 """
32 """
33
33
34 repo_name = match_dict.get('repo_name')
34 repo_name = match_dict.get('repo_name')
35 return is_valid_repo(repo_name, config['base_path'])
35 return is_valid_repo(repo_name, config['base_path'])
36
36
@@ -42,7 +42,7 b' def make_map(config):'
42 :param match_dict:
42 :param match_dict:
43 """
43 """
44 repos_group_name = match_dict.get('group_name')
44 repos_group_name = match_dict.get('group_name')
45
45
46 return is_valid_repos_group(repos_group_name, config['base_path'])
46 return is_valid_repos_group(repos_group_name, config['base_path'])
47
47
48
48
@@ -333,13 +333,13 b' def make_map(config):'
333 # REPOSITORY ROUTES
333 # REPOSITORY ROUTES
334 #==========================================================================
334 #==========================================================================
335 rmap.connect('summary_home', '/{repo_name:.*}',
335 rmap.connect('summary_home', '/{repo_name:.*}',
336 controller='summary',
336 controller='summary',
337 conditions=dict(function=check_repo))
337 conditions=dict(function=check_repo))
338
338
339 # rmap.connect('repo_group_home', '/{group_name:.*}',
339 rmap.connect('repos_group_home', '/{group_name:.*}',
340 # controller='admin/repos_groups',action="show_by_name",
340 controller='admin/repos_groups', action="show_by_name",
341 # conditions=dict(function=check_group))
341 conditions=dict(function=check_group))
342
342
343 rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
343 rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
344 controller='changeset', revision='tip',
344 controller='changeset', revision='tip',
345 conditions=dict(function=check_repo))
345 conditions=dict(function=check_repo))
@@ -33,14 +33,10 b' class ReposGroupsController(BaseControll'
33 def __load_defaults(self):
33 def __load_defaults(self):
34
34
35 c.repo_groups = [('', '')]
35 c.repo_groups = [('', '')]
36 parents_link = lambda k: h.literal('»'.join(
36 parents_link = lambda k: h.literal('»'.join(k))
37 map(lambda k: k.group_name,
38 k.parents + [k])
39 )
40 )
41
37
42 c.repo_groups.extend([(x.group_id, parents_link(x)) for \
38 c.repo_groups.extend([(x.group_id, parents_link(x.full_path_splitted))
43 x in self.sa.query(Group).all()])
39 for x in self.sa.query(Group).all()])
44
40
45 c.repo_groups = sorted(c.repo_groups,
41 c.repo_groups = sorted(c.repo_groups,
46 key=lambda t: t[1].split('»')[0])
42 key=lambda t: t[1].split('»')[0])
@@ -58,6 +54,8 b' class ReposGroupsController(BaseControll'
58
54
59 data = repo_group.get_dict()
55 data = repo_group.get_dict()
60
56
57 data['group_name'] = repo_group.name
58
61 return data
59 return data
62
60
63 @HasPermissionAnyDecorator('hg.admin')
61 @HasPermissionAnyDecorator('hg.admin')
@@ -176,6 +174,10 b' class ReposGroupsController(BaseControll'
176
174
177 return redirect(url('repos_groups'))
175 return redirect(url('repos_groups'))
178
176
177 def show_by_name(self, group_name):
178 id_ = Group.get_by_group_name(group_name).group_id
179 return self.show(id_)
180
179 def show(self, id, format='html'):
181 def show(self, id, format='html'):
180 """GET /repos_groups/id: Show a specific item"""
182 """GET /repos_groups/id: Show a specific item"""
181 # url('repos_group', id=ID)
183 # url('repos_group', id=ID)
@@ -360,16 +360,19 b' def map_groups(groups):'
360
360
361 parent = None
361 parent = None
362 group = None
362 group = None
363 for lvl, group_name in enumerate(groups[:-1]):
363
364 # last element is repo in nested groups structure
365 groups = groups[:-1]
366
367 for lvl, group_name in enumerate(groups):
368 group_name = '/'.join(groups[:lvl] + [group_name])
364 group = sa.query(Group).filter(Group.group_name == group_name).scalar()
369 group = sa.query(Group).filter(Group.group_name == group_name).scalar()
365
370
366 if group is None:
371 if group is None:
367 group = Group(group_name, parent)
372 group = Group(group_name, parent)
368 sa.add(group)
373 sa.add(group)
369 sa.commit()
374 sa.commit()
370
371 parent = group
375 parent = group
372
373 return group
376 return group
374
377
375
378
@@ -126,7 +126,8 b' class BaseModel(object):'
126
126
127 @classmethod
127 @classmethod
128 def get(cls, id_):
128 def get(cls, id_):
129 return Session.query(cls).get(id_)
129 if id_:
130 return Session.query(cls).get(id_)
130
131
131 @classmethod
132 @classmethod
132 def delete(cls, id_):
133 def delete(cls, id_):
@@ -721,6 +722,10 b' class Group(Base, BaseModel):'
721 def url_sep(cls):
722 def url_sep(cls):
722 return '/'
723 return '/'
723
724
725 @classmethod
726 def get_by_group_name(cls, group_name):
727 return cls.query().filter(cls.group_name == group_name).scalar()
728
724 @property
729 @property
725 def parents(self):
730 def parents(self):
726 parents_recursion_limit = 5
731 parents_recursion_limit = 5
@@ -750,9 +755,16 b' class Group(Base, BaseModel):'
750 return Session.query(Group).filter(Group.parent_group == self)
755 return Session.query(Group).filter(Group.parent_group == self)
751
756
752 @property
757 @property
758 def name(self):
759 return self.group_name.split(Group.url_sep())[-1]
760
761 @property
753 def full_path(self):
762 def full_path(self):
754 return Group.url_sep().join([g.group_name for g in self.parents] +
763 return self.group_name
755 [self.group_name])
764
765 @property
766 def full_path_splitted(self):
767 return self.group_name.split(Group.url_sep())
756
768
757 @property
769 @property
758 def repositories(self):
770 def repositories(self):
@@ -771,6 +783,17 b' class Group(Base, BaseModel):'
771
783
772 return cnt + children_count(self)
784 return cnt + children_count(self)
773
785
786
787 def get_new_name(self, group_name):
788 """
789 returns new full group name based on parent and new name
790
791 :param group_name:
792 """
793 path_prefix = self.parent_group.full_path_splitted if self.parent_group else []
794 return Group.url_sep().join(path_prefix + [group_name])
795
796
774 class Permission(Base, BaseModel):
797 class Permission(Base, BaseModel):
775 __tablename__ = 'permissions'
798 __tablename__ = 'permissions'
776 __table_args__ = {'extend_existing':True}
799 __table_args__ = {'extend_existing':True}
@@ -50,7 +50,7 b' class ReposGroupModel(BaseModel):'
50 q = RhodeCodeUi.get_by_key('/').one()
50 q = RhodeCodeUi.get_by_key('/').one()
51 return q.ui_value
51 return q.ui_value
52
52
53 def __create_group(self, group_name, parent_id):
53 def __create_group(self, group_name):
54 """
54 """
55 makes repositories group on filesystem
55 makes repositories group on filesystem
56
56
@@ -58,44 +58,30 b' class ReposGroupModel(BaseModel):'
58 :param parent_id:
58 :param parent_id:
59 """
59 """
60
60
61 if parent_id:
61 create_path = os.path.join(self.repos_path, group_name)
62 paths = Group.get(parent_id).full_path.split(Group.url_sep())
63 parent_path = os.sep.join(paths)
64 else:
65 parent_path = ''
66
67 create_path = os.path.join(self.repos_path, parent_path, group_name)
68 log.debug('creating new group in %s', create_path)
62 log.debug('creating new group in %s', create_path)
69
63
70 if os.path.isdir(create_path):
64 if os.path.isdir(create_path):
71 raise Exception('That directory already exists !')
65 raise Exception('That directory already exists !')
72
66
73
74 os.makedirs(create_path)
67 os.makedirs(create_path)
75
68
76
69 def __rename_group(self, old, new):
77 def __rename_group(self, old, old_parent_id, new, new_parent_id):
78 """
70 """
79 Renames a group on filesystem
71 Renames a group on filesystem
80
72
81 :param group_name:
73 :param group_name:
82 """
74 """
75
76 if old == new:
77 log.debug('skipping group rename')
78 return
79
83 log.debug('renaming repos group from %s to %s', old, new)
80 log.debug('renaming repos group from %s to %s', old, new)
84
81
85 if new_parent_id:
86 paths = Group.get(new_parent_id).full_path.split(Group.url_sep())
87 new_parent_path = os.sep.join(paths)
88 else:
89 new_parent_path = ''
90
82
91 if old_parent_id:
83 old_path = os.path.join(self.repos_path, old)
92 paths = Group.get(old_parent_id).full_path.split(Group.url_sep())
84 new_path = os.path.join(self.repos_path, new)
93 old_parent_path = os.sep.join(paths)
94 else:
95 old_parent_path = ''
96
97 old_path = os.path.join(self.repos_path, old_parent_path, old)
98 new_path = os.path.join(self.repos_path, new_parent_path, new)
99
85
100 log.debug('renaming repos paths from %s to %s', old_path, new_path)
86 log.debug('renaming repos paths from %s to %s', old_path, new_path)
101
87
@@ -119,17 +105,16 b' class ReposGroupModel(BaseModel):'
119 def create(self, form_data):
105 def create(self, form_data):
120 try:
106 try:
121 new_repos_group = Group()
107 new_repos_group = Group()
122 new_repos_group.group_name = form_data['group_name']
108 new_repos_group.group_description = form_data['group_description']
123 new_repos_group.group_description = \
109 new_repos_group.parent_group = Group.get(form_data['group_parent_id'])
124 form_data['group_description']
110 new_repos_group.group_name = new_repos_group.get_new_name(form_data['group_name'])
125 new_repos_group.group_parent_id = form_data['group_parent_id']
126
111
127 self.sa.add(new_repos_group)
112 self.sa.add(new_repos_group)
128
113
129 self.__create_group(form_data['group_name'],
114 self.__create_group(new_repos_group.group_name)
130 form_data['group_parent_id'])
131
115
132 self.sa.commit()
116 self.sa.commit()
117 return new_repos_group
133 except:
118 except:
134 log.error(traceback.format_exc())
119 log.error(traceback.format_exc())
135 self.sa.rollback()
120 self.sa.rollback()
@@ -139,23 +124,21 b' class ReposGroupModel(BaseModel):'
139
124
140 try:
125 try:
141 repos_group = Group.get(repos_group_id)
126 repos_group = Group.get(repos_group_id)
142 old_name = repos_group.group_name
127 old_path = repos_group.full_path
143 old_parent_id = repos_group.group_parent_id
144
128
145 repos_group.group_name = form_data['group_name']
129 #change properties
146 repos_group.group_description = \
130 repos_group.group_description = form_data['group_description']
147 form_data['group_description']
131 repos_group.parent_group = Group.get(form_data['group_parent_id'])
148 repos_group.group_parent_id = form_data['group_parent_id']
132 repos_group.group_name = repos_group.get_new_name(form_data['group_name'])
133
134 new_path = repos_group.full_path
149
135
150 self.sa.add(repos_group)
136 self.sa.add(repos_group)
151
137
152 if old_name != form_data['group_name'] or (old_parent_id !=
138 self.__rename_group(old_path, new_path)
153 form_data['group_parent_id']):
154 self.__rename_group(old=old_name, old_parent_id=old_parent_id,
155 new=form_data['group_name'],
156 new_parent_id=form_data['group_parent_id'])
157
139
158 self.sa.commit()
140 self.sa.commit()
141 return repos_group
159 except:
142 except:
160 log.error(traceback.format_exc())
143 log.error(traceback.format_exc())
161 self.sa.rollback()
144 self.sa.rollback()
@@ -564,6 +564,14 b' padding:12px 9px 7px 24px;'
564 }
564 }
565
565
566
566
567 .groups_breadcrumbs a {
568 color: #fff;
569 }
570 .groups_breadcrumbs a:hover {
571 color: #bfe3ff;
572 text-decoration: none;
573 }
574
567 .quick_repo_menu{
575 .quick_repo_menu{
568 background: #FFF url("../images/vertical-indicator.png") 8px 50% no-repeat !important;
576 background: #FFF url("../images/vertical-indicator.png") 8px 50% no-repeat !important;
569 cursor: pointer;
577 cursor: pointer;
@@ -5,12 +5,14 b''
5 </%def>
5 </%def>
6
6
7 <%def name="breadcrumbs()">
7 <%def name="breadcrumbs()">
8 <span class="groups_breadcrumbs">
8 ${_('Groups')}
9 ${_('Groups')}
9 %if c.group.parent_group:
10 %if c.group.parent_group:
10 &raquo; ${h.link_to(c.group.parent_group.group_name,
11 &raquo; ${h.link_to(c.group.parent_group.name,
11 h.url('repos_group',id=c.group.parent_group.group_id))}
12 h.url('repos_group_home',group_name=c.group.parent_group.group_name))}
12 %endif
13 %endif
13 &raquo; "${c.group.group_name}" ${_('with')}
14 &raquo; "${c.group.name}" ${_('with')}
15 </span>
14 </%def>
16 </%def>
15
17
16 <%def name="page_nav()">
18 <%def name="page_nav()">
@@ -2,14 +2,14 b''
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Edit repos group')} ${c.repos_group.group_name} - ${c.rhodecode_name}
5 ${_('Edit repos group')} ${c.repos_group.name} - ${c.rhodecode_name}
6 </%def>
6 </%def>
7 <%def name="breadcrumbs_links()">
7 <%def name="breadcrumbs_links()">
8 ${h.link_to(_('Admin'),h.url('admin_home'))}
8 ${h.link_to(_('Admin'),h.url('admin_home'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(_('Repos groups'),h.url('repos_groups'))}
10 ${h.link_to(_('Repos groups'),h.url('repos_groups'))}
11 &raquo;
11 &raquo;
12 ${_('edit repos group')} "${c.repos_group.group_name}"
12 ${_('edit repos group')} "${c.repos_group.name}"
13 </%def>
13 </%def>
14
14
15 <%def name="page_nav()">
15 <%def name="page_nav()">
@@ -44,14 +44,14 b''
44 <td>
44 <td>
45 <div style="white-space: nowrap">
45 <div style="white-space: nowrap">
46 <img class="icon" alt="${_('Repositories group')}" src="${h.url('/images/icons/database_link.png')}"/>
46 <img class="icon" alt="${_('Repositories group')}" src="${h.url('/images/icons/database_link.png')}"/>
47 ${h.link_to(h.literal(' &raquo; '.join([g.group_name for g in gr.parents+[gr]])),url('edit_repos_group',id=gr.group_id))}
47 ${h.link_to(h.literal(' &raquo; '.join([g.name for g in gr.parents+[gr]])),url('edit_repos_group',id=gr.group_id))}
48 </div>
48 </div>
49 </td>
49 </td>
50 <td>${gr.group_description}</td>
50 <td>${gr.group_description}</td>
51 <td><b>${gr.repositories.count()}</b></td>
51 <td><b>${gr.repositories.count()}</b></td>
52 <td>
52 <td>
53 ${h.form(url('repos_group', id=gr.group_id),method='delete')}
53 ${h.form(url('repos_group', id=gr.group_id),method='delete')}
54 ${h.submit('remove_%s' % gr.group_name,'delete',class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this group')+"');")}
54 ${h.submit('remove_%s' % gr.name,'delete',class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this group')+"');")}
55 ${h.end_form()}
55 ${h.end_form()}
56 </td>
56 </td>
57 </tr>
57 </tr>
@@ -35,7 +35,7 b''
35 <td>
35 <td>
36 <div style="white-space: nowrap">
36 <div style="white-space: nowrap">
37 <img class="icon" alt="${_('Repositories group')}" src="${h.url('/images/icons/database_link.png')}"/>
37 <img class="icon" alt="${_('Repositories group')}" src="${h.url('/images/icons/database_link.png')}"/>
38 ${h.link_to(gr.group_name,url('repos_group',id=gr.group_id))}
38 ${h.link_to(gr.name,url('repos_group_home',group_name=gr.group_name))}
39 </div>
39 </div>
40 </td>
40 </td>
41 <td>${gr.group_description}</td>
41 <td>${gr.group_description}</td>
@@ -1,2 +1,115 b''
1 import os
1 import unittest
2 import unittest
2 from rhodecode.tests import *
3 from rhodecode.tests import *
4
5 from rhodecode.model.repos_group import ReposGroupModel
6 from rhodecode.model.db import Group
7 from sqlalchemy.exc import IntegrityError
8
9 class TestReposGroups(unittest.TestCase):
10
11 def setUp(self):
12 self.g1 = self.__make_group('test1', skip_if_exists=True)
13 self.g2 = self.__make_group('test2', skip_if_exists=True)
14 self.g3 = self.__make_group('test3', skip_if_exists=True)
15
16 def tearDown(self):
17 print 'out'
18
19 def __check_path(self, *path):
20 path = [TESTS_TMP_PATH] + list(path)
21 path = os.path.join(*path)
22 return os.path.isdir(path)
23
24 def _check_folders(self):
25 print os.listdir(TESTS_TMP_PATH)
26
27 def __make_group(self, path, desc='desc', parent_id=None,
28 skip_if_exists=False):
29
30 gr = Group.get_by_group_name(path)
31 if gr and skip_if_exists:
32 return gr
33
34 form_data = dict(group_name=path,
35 group_description=desc,
36 group_parent_id=parent_id)
37 gr = ReposGroupModel().create(form_data)
38 return gr
39
40 def __delete_group(self, id_):
41 ReposGroupModel().delete(id_)
42
43
44 def __update_group(self, id_, path, desc='desc', parent_id=None):
45 form_data = dict(group_name=path,
46 group_description=desc,
47 group_parent_id=parent_id)
48
49 gr = ReposGroupModel().update(id_, form_data)
50 return gr
51
52 def test_create_group(self):
53 g = self.__make_group('newGroup')
54 self.assertEqual(g.full_path, 'newGroup')
55
56 self.assertTrue(self.__check_path('newGroup'))
57
58
59 def test_create_same_name_group(self):
60 self.assertRaises(IntegrityError, lambda:self.__make_group('newGroup'))
61
62
63 def test_same_subgroup(self):
64 sg1 = self.__make_group('sub1', parent_id=self.g1.group_id)
65 self.assertEqual(sg1.parent_group, self.g1)
66 self.assertEqual(sg1.full_path, 'test1/sub1')
67 self.assertTrue(self.__check_path('test1', 'sub1'))
68
69 ssg1 = self.__make_group('subsub1', parent_id=sg1.group_id)
70 self.assertEqual(ssg1.parent_group, sg1)
71 self.assertEqual(ssg1.full_path, 'test1/sub1/subsub1')
72 self.assertTrue(self.__check_path('test1', 'sub1', 'subsub1'))
73
74
75 def test_remove_group(self):
76 sg1 = self.__make_group('deleteme')
77 self.__delete_group(sg1.group_id)
78
79 self.assertEqual(Group.get(sg1.group_id), None)
80 self.assertFalse(self.__check_path('deteteme'))
81
82 sg1 = self.__make_group('deleteme', parent_id=self.g1.group_id)
83 self.__delete_group(sg1.group_id)
84
85 self.assertEqual(Group.get(sg1.group_id), None)
86 self.assertFalse(self.__check_path('test1', 'deteteme'))
87
88
89 def test_rename_single_group(self):
90 sg1 = self.__make_group('initial')
91
92 new_sg1 = self.__update_group(sg1.group_id, 'after')
93 self.assertTrue(self.__check_path('after'))
94 self.assertEqual(Group.get_by_group_name('initial'), None)
95
96
97 def test_update_group_parent(self):
98
99 sg1 = self.__make_group('initial', parent_id=self.g1.group_id)
100
101 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g1.group_id)
102 self.assertTrue(self.__check_path('test1', 'after'))
103 self.assertEqual(Group.get_by_group_name('test1/initial'), None)
104
105
106 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g3.group_id)
107 self.assertTrue(self.__check_path('test3', 'after'))
108 self.assertEqual(Group.get_by_group_name('test3/initial'), None)
109
110
111 new_sg1 = self.__update_group(sg1.group_id, 'hello')
112 self.assertTrue(self.__check_path('hello'))
113
114 self.assertEqual(Group.get_by_group_name('hello'), new_sg1)
115
General Comments 0
You need to be logged in to leave comments. Login now