Commits

Marcin Kuzminski committed 27be8f9

implements #226 repo groups available by path
fixes #259 Groups with the same name but with different parent group

  • Participants
  • Parent commits c307c92
  • Branches beta

Comments (0)

Files changed (11)

File rhodecode/config/routing.py

                  always_scan=config['debug'])
     rmap.minimization = False
     rmap.explicit = False
-    
+
     from rhodecode.lib.utils import is_valid_repo
     from rhodecode.lib.utils import is_valid_repos_group
-    
+
     def check_repo(environ, match_dict):
         """
         check for valid repository for proper 404 handling
         :param environ:
         :param match_dict:
         """
-         
+
         repo_name = match_dict.get('repo_name')
         return is_valid_repo(repo_name, config['base_path'])
 
         :param match_dict:
         """
         repos_group_name = match_dict.get('group_name')
-        
+
         return is_valid_repos_group(repos_group_name, config['base_path'])
 
 
     # REPOSITORY ROUTES
     #==========================================================================
     rmap.connect('summary_home', '/{repo_name:.*}',
-                controller='summary', 
+                controller='summary',
                 conditions=dict(function=check_repo))
-    
-#    rmap.connect('repo_group_home', '/{group_name:.*}',
-#                controller='admin/repos_groups',action="show_by_name", 
-#                conditions=dict(function=check_group))
-    
+
+    rmap.connect('repos_group_home', '/{group_name:.*}',
+                controller='admin/repos_groups', action="show_by_name",
+                conditions=dict(function=check_group))
+
     rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
                 controller='changeset', revision='tip',
                 conditions=dict(function=check_repo))

File rhodecode/controllers/admin/repos_groups.py

     def __load_defaults(self):
 
         c.repo_groups = [('', '')]
-        parents_link = lambda k: h.literal('»'.join(
-                                    map(lambda k: k.group_name,
-                                        k.parents + [k])
-                                    )
-                                )
+        parents_link = lambda k: h.literal('»'.join(k))
 
-        c.repo_groups.extend([(x.group_id, parents_link(x)) for \
-                                            x in self.sa.query(Group).all()])
+        c.repo_groups.extend([(x.group_id, parents_link(x.full_path_splitted))
+                              for x in self.sa.query(Group).all()])
 
         c.repo_groups = sorted(c.repo_groups,
                                key=lambda t: t[1].split('»')[0])
 
         data = repo_group.get_dict()
 
+        data['group_name'] = repo_group.name
+
         return data
 
     @HasPermissionAnyDecorator('hg.admin')
 
         return redirect(url('repos_groups'))
 
+    def show_by_name(self, group_name):
+        id_ = Group.get_by_group_name(group_name).group_id
+        return self.show(id_)
+
     def show(self, id, format='html'):
         """GET /repos_groups/id: Show a specific item"""
         # url('repos_group', id=ID)

File rhodecode/lib/utils.py

 
     parent = None
     group = None
-    for lvl, group_name in enumerate(groups[:-1]):
+
+    # last element is repo in nested groups structure
+    groups = groups[:-1]
+
+    for lvl, group_name in enumerate(groups):
+        group_name = '/'.join(groups[:lvl] + [group_name])
         group = sa.query(Group).filter(Group.group_name == group_name).scalar()
 
         if group is None:
             group = Group(group_name, parent)
             sa.add(group)
             sa.commit()
-
         parent = group
-
     return group
 
 

File rhodecode/model/db.py

 
     @classmethod
     def get(cls, id_):
-        return Session.query(cls).get(id_)
+        if id_:
+            return Session.query(cls).get(id_)
 
     @classmethod
     def delete(cls, id_):
     def url_sep(cls):
         return '/'
 
+    @classmethod
+    def get_by_group_name(cls, group_name):
+        return cls.query().filter(cls.group_name == group_name).scalar()
+
     @property
     def parents(self):
         parents_recursion_limit = 5
         return Session.query(Group).filter(Group.parent_group == self)
 
     @property
+    def name(self):
+        return self.group_name.split(Group.url_sep())[-1]
+
+    @property
     def full_path(self):
-        return Group.url_sep().join([g.group_name for g in self.parents] +
-                        [self.group_name])
+        return self.group_name
+
+    @property
+    def full_path_splitted(self):
+        return self.group_name.split(Group.url_sep())
 
     @property
     def repositories(self):
 
         return cnt + children_count(self)
 
+
+    def get_new_name(self, group_name):
+        """
+        returns new full group name based on parent and new name
+        
+        :param group_name:
+        """
+        path_prefix = self.parent_group.full_path_splitted if self.parent_group else []
+        return Group.url_sep().join(path_prefix + [group_name])
+
+
 class Permission(Base, BaseModel):
     __tablename__ = 'permissions'
     __table_args__ = {'extend_existing':True}

File rhodecode/model/repos_group.py

         q = RhodeCodeUi.get_by_key('/').one()
         return q.ui_value
 
-    def __create_group(self, group_name, parent_id):
+    def __create_group(self, group_name):
         """
         makes repositories group on filesystem
 
         :param parent_id:
         """
 
-        if parent_id:
-            paths = Group.get(parent_id).full_path.split(Group.url_sep())
-            parent_path = os.sep.join(paths)
-        else:
-            parent_path = ''
-
-        create_path = os.path.join(self.repos_path, parent_path, group_name)
+        create_path = os.path.join(self.repos_path, group_name)
         log.debug('creating new group in %s', create_path)
 
         if os.path.isdir(create_path):
             raise Exception('That directory already exists !')
 
-
         os.makedirs(create_path)
 
-
-    def __rename_group(self, old, old_parent_id, new, new_parent_id):
+    def __rename_group(self, old, new):
         """
         Renames a group on filesystem
         
         :param group_name:
         """
+
+        if old == new:
+            log.debug('skipping group rename')
+            return
+
         log.debug('renaming repos group from %s to %s', old, new)
 
-        if new_parent_id:
-            paths = Group.get(new_parent_id).full_path.split(Group.url_sep())
-            new_parent_path = os.sep.join(paths)
-        else:
-            new_parent_path = ''
 
-        if old_parent_id:
-            paths = Group.get(old_parent_id).full_path.split(Group.url_sep())
-            old_parent_path = os.sep.join(paths)
-        else:
-            old_parent_path = ''
-
-        old_path = os.path.join(self.repos_path, old_parent_path, old)
-        new_path = os.path.join(self.repos_path, new_parent_path, new)
+        old_path = os.path.join(self.repos_path, old)
+        new_path = os.path.join(self.repos_path, new)
 
         log.debug('renaming repos paths from %s to %s', old_path, new_path)
 
     def create(self, form_data):
         try:
             new_repos_group = Group()
-            new_repos_group.group_name = form_data['group_name']
-            new_repos_group.group_description = \
-                form_data['group_description']
-            new_repos_group.group_parent_id = form_data['group_parent_id']
+            new_repos_group.group_description = form_data['group_description']
+            new_repos_group.parent_group = Group.get(form_data['group_parent_id'])
+            new_repos_group.group_name = new_repos_group.get_new_name(form_data['group_name'])
 
             self.sa.add(new_repos_group)
 
-            self.__create_group(form_data['group_name'],
-                                form_data['group_parent_id'])
+            self.__create_group(new_repos_group.group_name)
 
             self.sa.commit()
+            return new_repos_group
         except:
             log.error(traceback.format_exc())
             self.sa.rollback()
 
         try:
             repos_group = Group.get(repos_group_id)
-            old_name = repos_group.group_name
-            old_parent_id = repos_group.group_parent_id
+            old_path = repos_group.full_path
 
-            repos_group.group_name = form_data['group_name']
-            repos_group.group_description = \
-                form_data['group_description']
-            repos_group.group_parent_id = form_data['group_parent_id']
+            #change properties
+            repos_group.group_description = form_data['group_description']
+            repos_group.parent_group = Group.get(form_data['group_parent_id'])
+            repos_group.group_name = repos_group.get_new_name(form_data['group_name'])
+
+            new_path = repos_group.full_path
 
             self.sa.add(repos_group)
 
-            if old_name != form_data['group_name'] or (old_parent_id !=
-                                                form_data['group_parent_id']):
-                self.__rename_group(old=old_name, old_parent_id=old_parent_id,
-                                    new=form_data['group_name'],
-                                    new_parent_id=form_data['group_parent_id'])
+            self.__rename_group(old_path, new_path)
 
             self.sa.commit()
+            return repos_group
         except:
             log.error(traceback.format_exc())
             self.sa.rollback()

File rhodecode/public/css/style.css

 }
 
 
+.groups_breadcrumbs a {
+	color: #fff;
+}
+.groups_breadcrumbs a:hover {
+    color: #bfe3ff;
+    text-decoration: none;
+}
+
 .quick_repo_menu{
 	background: #FFF url("../images/vertical-indicator.png") 8px 50% no-repeat !important;
 	cursor: pointer;

File rhodecode/templates/admin/repos_groups/repos_groups.html

 </%def>
 
 <%def name="breadcrumbs()">
+    <span class="groups_breadcrumbs">
     ${_('Groups')} 
     %if c.group.parent_group:
-        &raquo; ${h.link_to(c.group.parent_group.group_name,
-        h.url('repos_group',id=c.group.parent_group.group_id))}
+        &raquo; ${h.link_to(c.group.parent_group.name,
+        h.url('repos_group_home',group_name=c.group.parent_group.group_name))}
     %endif
-    &raquo; "${c.group.group_name}" ${_('with')} 
+    &raquo; "${c.group.name}" ${_('with')}
+    </span> 
 </%def>
 
 <%def name="page_nav()">

File rhodecode/templates/admin/repos_groups/repos_groups_edit.html

 <%inherit file="/base/base.html"/>
 
 <%def name="title()">
-    ${_('Edit repos group')} ${c.repos_group.group_name} - ${c.rhodecode_name}
+    ${_('Edit repos group')} ${c.repos_group.name} - ${c.rhodecode_name}
 </%def>
 <%def name="breadcrumbs_links()">
     ${h.link_to(_('Admin'),h.url('admin_home'))} 
     &raquo; 
     ${h.link_to(_('Repos groups'),h.url('repos_groups'))} 
     &raquo;
-    ${_('edit repos group')} "${c.repos_group.group_name}"
+    ${_('edit repos group')} "${c.repos_group.name}"
 </%def>
 
 <%def name="page_nav()">

File rhodecode/templates/admin/repos_groups/repos_groups_show.html

                       <td>
                           <div style="white-space: nowrap">
                           <img class="icon" alt="${_('Repositories group')}" src="${h.url('/images/icons/database_link.png')}"/>
-                          ${h.link_to(h.literal(' &raquo; '.join([g.group_name for g in gr.parents+[gr]])),url('edit_repos_group',id=gr.group_id))}
+                          ${h.link_to(h.literal(' &raquo; '.join([g.name for g in gr.parents+[gr]])),url('edit_repos_group',id=gr.group_id))}
                           </div>
                       </td>
                       <td>${gr.group_description}</td>
                       <td><b>${gr.repositories.count()}</b></td>
 		               <td>
 		                 ${h.form(url('repos_group', id=gr.group_id),method='delete')}
-		                   ${h.submit('remove_%s' % gr.group_name,'delete',class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this group')+"');")}
+		                   ${h.submit('remove_%s' % gr.name,'delete',class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this group')+"');")}
 		                 ${h.end_form()}
 		               </td>                      
                   </tr>

File rhodecode/templates/index_base.html

                       <td>
                           <div style="white-space: nowrap">
                           <img class="icon" alt="${_('Repositories group')}" src="${h.url('/images/icons/database_link.png')}"/>
-                          ${h.link_to(gr.group_name,url('repos_group',id=gr.group_id))}
+                          ${h.link_to(gr.name,url('repos_group_home',group_name=gr.group_name))}
                           </div>
                       </td>
                       <td>${gr.group_description}</td>

File rhodecode/tests/test_models.py

+import os
 import unittest
 from rhodecode.tests import *
+
+from rhodecode.model.repos_group import ReposGroupModel
+from rhodecode.model.db import Group
+from sqlalchemy.exc import IntegrityError
+
+class TestReposGroups(unittest.TestCase):
+
+    def setUp(self):
+        self.g1 = self.__make_group('test1', skip_if_exists=True)
+        self.g2 = self.__make_group('test2', skip_if_exists=True)
+        self.g3 = self.__make_group('test3', skip_if_exists=True)
+
+    def tearDown(self):
+        print 'out'
+
+    def __check_path(self, *path):
+        path = [TESTS_TMP_PATH] + list(path)
+        path = os.path.join(*path)
+        return os.path.isdir(path)
+
+    def _check_folders(self):
+        print os.listdir(TESTS_TMP_PATH)
+
+    def __make_group(self, path, desc='desc', parent_id=None,
+                     skip_if_exists=False):
+
+        gr = Group.get_by_group_name(path)
+        if gr and skip_if_exists:
+            return gr
+
+        form_data = dict(group_name=path,
+                         group_description=desc,
+                         group_parent_id=parent_id)
+        gr = ReposGroupModel().create(form_data)
+        return gr
+
+    def __delete_group(self, id_):
+        ReposGroupModel().delete(id_)
+
+
+    def __update_group(self, id_, path, desc='desc', parent_id=None):
+        form_data = dict(group_name=path,
+                         group_description=desc,
+                         group_parent_id=parent_id)
+
+        gr = ReposGroupModel().update(id_, form_data)
+        return gr
+
+    def test_create_group(self):
+        g = self.__make_group('newGroup')
+        self.assertEqual(g.full_path, 'newGroup')
+
+        self.assertTrue(self.__check_path('newGroup'))
+
+
+    def test_create_same_name_group(self):
+        self.assertRaises(IntegrityError, lambda:self.__make_group('newGroup'))
+
+
+    def test_same_subgroup(self):
+        sg1 = self.__make_group('sub1', parent_id=self.g1.group_id)
+        self.assertEqual(sg1.parent_group, self.g1)
+        self.assertEqual(sg1.full_path, 'test1/sub1')
+        self.assertTrue(self.__check_path('test1', 'sub1'))
+
+        ssg1 = self.__make_group('subsub1', parent_id=sg1.group_id)
+        self.assertEqual(ssg1.parent_group, sg1)
+        self.assertEqual(ssg1.full_path, 'test1/sub1/subsub1')
+        self.assertTrue(self.__check_path('test1', 'sub1', 'subsub1'))
+
+
+    def test_remove_group(self):
+        sg1 = self.__make_group('deleteme')
+        self.__delete_group(sg1.group_id)
+
+        self.assertEqual(Group.get(sg1.group_id), None)
+        self.assertFalse(self.__check_path('deteteme'))
+
+        sg1 = self.__make_group('deleteme', parent_id=self.g1.group_id)
+        self.__delete_group(sg1.group_id)
+
+        self.assertEqual(Group.get(sg1.group_id), None)
+        self.assertFalse(self.__check_path('test1', 'deteteme'))
+
+
+    def test_rename_single_group(self):
+        sg1 = self.__make_group('initial')
+
+        new_sg1 = self.__update_group(sg1.group_id, 'after')
+        self.assertTrue(self.__check_path('after'))
+        self.assertEqual(Group.get_by_group_name('initial'), None)
+
+
+    def test_update_group_parent(self):
+
+        sg1 = self.__make_group('initial', parent_id=self.g1.group_id)
+
+        new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g1.group_id)
+        self.assertTrue(self.__check_path('test1', 'after'))
+        self.assertEqual(Group.get_by_group_name('test1/initial'), None)
+
+
+        new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g3.group_id)
+        self.assertTrue(self.__check_path('test3', 'after'))
+        self.assertEqual(Group.get_by_group_name('test3/initial'), None)
+
+
+        new_sg1 = self.__update_group(sg1.group_id, 'hello')
+        self.assertTrue(self.__check_path('hello'))
+
+        self.assertEqual(Group.get_by_group_name('hello'), new_sg1)
+