##// END OF EJS Templates
ui: normalize main grid sizes and columns
marcink -
r3557:77e4ef40 default
parent child Browse files
Show More
@@ -1,773 +1,777 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 """
22 """
23 repo group model for RhodeCode
23 repo group model for RhodeCode
24 """
24 """
25
25
26 import os
26 import os
27 import datetime
27 import datetime
28 import itertools
28 import itertools
29 import logging
29 import logging
30 import shutil
30 import shutil
31 import traceback
31 import traceback
32 import string
32 import string
33
33
34 from zope.cachedescriptors.property import Lazy as LazyProperty
34 from zope.cachedescriptors.property import Lazy as LazyProperty
35
35
36 from rhodecode import events
36 from rhodecode import events
37 from rhodecode.model import BaseModel
37 from rhodecode.model import BaseModel
38 from rhodecode.model.db import (_hash_key,
38 from rhodecode.model.db import (_hash_key,
39 RepoGroup, UserRepoGroupToPerm, User, Permission, UserGroupRepoGroupToPerm,
39 RepoGroup, UserRepoGroupToPerm, User, Permission, UserGroupRepoGroupToPerm,
40 UserGroup, Repository)
40 UserGroup, Repository)
41 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
41 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
42 from rhodecode.lib.caching_query import FromCache
42 from rhodecode.lib.caching_query import FromCache
43 from rhodecode.lib.utils2 import action_logger_generic, datetime_to_time
43 from rhodecode.lib.utils2 import action_logger_generic, datetime_to_time
44
44
45 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
46
46
47
47
48 class RepoGroupModel(BaseModel):
48 class RepoGroupModel(BaseModel):
49
49
50 cls = RepoGroup
50 cls = RepoGroup
51 PERSONAL_GROUP_DESC = 'personal repo group of user `%(username)s`'
51 PERSONAL_GROUP_DESC = 'personal repo group of user `%(username)s`'
52 PERSONAL_GROUP_PATTERN = '${username}' # default
52 PERSONAL_GROUP_PATTERN = '${username}' # default
53
53
54 def _get_user_group(self, users_group):
54 def _get_user_group(self, users_group):
55 return self._get_instance(UserGroup, users_group,
55 return self._get_instance(UserGroup, users_group,
56 callback=UserGroup.get_by_group_name)
56 callback=UserGroup.get_by_group_name)
57
57
58 def _get_repo_group(self, repo_group):
58 def _get_repo_group(self, repo_group):
59 return self._get_instance(RepoGroup, repo_group,
59 return self._get_instance(RepoGroup, repo_group,
60 callback=RepoGroup.get_by_group_name)
60 callback=RepoGroup.get_by_group_name)
61
61
62 @LazyProperty
62 @LazyProperty
63 def repos_path(self):
63 def repos_path(self):
64 """
64 """
65 Gets the repositories root path from database
65 Gets the repositories root path from database
66 """
66 """
67
67
68 settings_model = VcsSettingsModel(sa=self.sa)
68 settings_model = VcsSettingsModel(sa=self.sa)
69 return settings_model.get_repos_location()
69 return settings_model.get_repos_location()
70
70
71 def get_by_group_name(self, repo_group_name, cache=None):
71 def get_by_group_name(self, repo_group_name, cache=None):
72 repo = self.sa.query(RepoGroup) \
72 repo = self.sa.query(RepoGroup) \
73 .filter(RepoGroup.group_name == repo_group_name)
73 .filter(RepoGroup.group_name == repo_group_name)
74
74
75 if cache:
75 if cache:
76 name_key = _hash_key(repo_group_name)
76 name_key = _hash_key(repo_group_name)
77 repo = repo.options(
77 repo = repo.options(
78 FromCache("sql_cache_short", "get_repo_group_%s" % name_key))
78 FromCache("sql_cache_short", "get_repo_group_%s" % name_key))
79 return repo.scalar()
79 return repo.scalar()
80
80
81 def get_default_create_personal_repo_group(self):
81 def get_default_create_personal_repo_group(self):
82 value = SettingsModel().get_setting_by_name(
82 value = SettingsModel().get_setting_by_name(
83 'create_personal_repo_group')
83 'create_personal_repo_group')
84 return value.app_settings_value if value else None or False
84 return value.app_settings_value if value else None or False
85
85
86 def get_personal_group_name_pattern(self):
86 def get_personal_group_name_pattern(self):
87 value = SettingsModel().get_setting_by_name(
87 value = SettingsModel().get_setting_by_name(
88 'personal_repo_group_pattern')
88 'personal_repo_group_pattern')
89 val = value.app_settings_value if value else None
89 val = value.app_settings_value if value else None
90 group_template = val or self.PERSONAL_GROUP_PATTERN
90 group_template = val or self.PERSONAL_GROUP_PATTERN
91
91
92 group_template = group_template.lstrip('/')
92 group_template = group_template.lstrip('/')
93 return group_template
93 return group_template
94
94
95 def get_personal_group_name(self, user):
95 def get_personal_group_name(self, user):
96 template = self.get_personal_group_name_pattern()
96 template = self.get_personal_group_name_pattern()
97 return string.Template(template).safe_substitute(
97 return string.Template(template).safe_substitute(
98 username=user.username,
98 username=user.username,
99 user_id=user.user_id,
99 user_id=user.user_id,
100 )
100 )
101
101
102 def create_personal_repo_group(self, user, commit_early=True):
102 def create_personal_repo_group(self, user, commit_early=True):
103 desc = self.PERSONAL_GROUP_DESC % {'username': user.username}
103 desc = self.PERSONAL_GROUP_DESC % {'username': user.username}
104 personal_repo_group_name = self.get_personal_group_name(user)
104 personal_repo_group_name = self.get_personal_group_name(user)
105
105
106 # create a new one
106 # create a new one
107 RepoGroupModel().create(
107 RepoGroupModel().create(
108 group_name=personal_repo_group_name,
108 group_name=personal_repo_group_name,
109 group_description=desc,
109 group_description=desc,
110 owner=user.username,
110 owner=user.username,
111 personal=True,
111 personal=True,
112 commit_early=commit_early)
112 commit_early=commit_early)
113
113
114 def _create_default_perms(self, new_group):
114 def _create_default_perms(self, new_group):
115 # create default permission
115 # create default permission
116 default_perm = 'group.read'
116 default_perm = 'group.read'
117 def_user = User.get_default_user()
117 def_user = User.get_default_user()
118 for p in def_user.user_perms:
118 for p in def_user.user_perms:
119 if p.permission.permission_name.startswith('group.'):
119 if p.permission.permission_name.startswith('group.'):
120 default_perm = p.permission.permission_name
120 default_perm = p.permission.permission_name
121 break
121 break
122
122
123 repo_group_to_perm = UserRepoGroupToPerm()
123 repo_group_to_perm = UserRepoGroupToPerm()
124 repo_group_to_perm.permission = Permission.get_by_key(default_perm)
124 repo_group_to_perm.permission = Permission.get_by_key(default_perm)
125
125
126 repo_group_to_perm.group = new_group
126 repo_group_to_perm.group = new_group
127 repo_group_to_perm.user_id = def_user.user_id
127 repo_group_to_perm.user_id = def_user.user_id
128 return repo_group_to_perm
128 return repo_group_to_perm
129
129
130 def _get_group_name_and_parent(self, group_name_full, repo_in_path=False,
130 def _get_group_name_and_parent(self, group_name_full, repo_in_path=False,
131 get_object=False):
131 get_object=False):
132 """
132 """
133 Get's the group name and a parent group name from given group name.
133 Get's the group name and a parent group name from given group name.
134 If repo_in_path is set to truth, we asume the full path also includes
134 If repo_in_path is set to truth, we asume the full path also includes
135 repo name, in such case we clean the last element.
135 repo name, in such case we clean the last element.
136
136
137 :param group_name_full:
137 :param group_name_full:
138 """
138 """
139 split_paths = 1
139 split_paths = 1
140 if repo_in_path:
140 if repo_in_path:
141 split_paths = 2
141 split_paths = 2
142 _parts = group_name_full.rsplit(RepoGroup.url_sep(), split_paths)
142 _parts = group_name_full.rsplit(RepoGroup.url_sep(), split_paths)
143
143
144 if repo_in_path and len(_parts) > 1:
144 if repo_in_path and len(_parts) > 1:
145 # such case last element is the repo_name
145 # such case last element is the repo_name
146 _parts.pop(-1)
146 _parts.pop(-1)
147 group_name_cleaned = _parts[-1] # just the group name
147 group_name_cleaned = _parts[-1] # just the group name
148 parent_repo_group_name = None
148 parent_repo_group_name = None
149
149
150 if len(_parts) > 1:
150 if len(_parts) > 1:
151 parent_repo_group_name = _parts[0]
151 parent_repo_group_name = _parts[0]
152
152
153 parent_group = None
153 parent_group = None
154 if parent_repo_group_name:
154 if parent_repo_group_name:
155 parent_group = RepoGroup.get_by_group_name(parent_repo_group_name)
155 parent_group = RepoGroup.get_by_group_name(parent_repo_group_name)
156
156
157 if get_object:
157 if get_object:
158 return group_name_cleaned, parent_repo_group_name, parent_group
158 return group_name_cleaned, parent_repo_group_name, parent_group
159
159
160 return group_name_cleaned, parent_repo_group_name
160 return group_name_cleaned, parent_repo_group_name
161
161
162 def check_exist_filesystem(self, group_name, exc_on_failure=True):
162 def check_exist_filesystem(self, group_name, exc_on_failure=True):
163 create_path = os.path.join(self.repos_path, group_name)
163 create_path = os.path.join(self.repos_path, group_name)
164 log.debug('creating new group in %s', create_path)
164 log.debug('creating new group in %s', create_path)
165
165
166 if os.path.isdir(create_path):
166 if os.path.isdir(create_path):
167 if exc_on_failure:
167 if exc_on_failure:
168 abs_create_path = os.path.abspath(create_path)
168 abs_create_path = os.path.abspath(create_path)
169 raise Exception('Directory `{}` already exists !'.format(abs_create_path))
169 raise Exception('Directory `{}` already exists !'.format(abs_create_path))
170 return False
170 return False
171 return True
171 return True
172
172
173 def _create_group(self, group_name):
173 def _create_group(self, group_name):
174 """
174 """
175 makes repository group on filesystem
175 makes repository group on filesystem
176
176
177 :param repo_name:
177 :param repo_name:
178 :param parent_id:
178 :param parent_id:
179 """
179 """
180
180
181 self.check_exist_filesystem(group_name)
181 self.check_exist_filesystem(group_name)
182 create_path = os.path.join(self.repos_path, group_name)
182 create_path = os.path.join(self.repos_path, group_name)
183 log.debug('creating new group in %s', create_path)
183 log.debug('creating new group in %s', create_path)
184 os.makedirs(create_path, mode=0o755)
184 os.makedirs(create_path, mode=0o755)
185 log.debug('created group in %s', create_path)
185 log.debug('created group in %s', create_path)
186
186
187 def _rename_group(self, old, new):
187 def _rename_group(self, old, new):
188 """
188 """
189 Renames a group on filesystem
189 Renames a group on filesystem
190
190
191 :param group_name:
191 :param group_name:
192 """
192 """
193
193
194 if old == new:
194 if old == new:
195 log.debug('skipping group rename')
195 log.debug('skipping group rename')
196 return
196 return
197
197
198 log.debug('renaming repository group from %s to %s', old, new)
198 log.debug('renaming repository group from %s to %s', old, new)
199
199
200 old_path = os.path.join(self.repos_path, old)
200 old_path = os.path.join(self.repos_path, old)
201 new_path = os.path.join(self.repos_path, new)
201 new_path = os.path.join(self.repos_path, new)
202
202
203 log.debug('renaming repos paths from %s to %s', old_path, new_path)
203 log.debug('renaming repos paths from %s to %s', old_path, new_path)
204
204
205 if os.path.isdir(new_path):
205 if os.path.isdir(new_path):
206 raise Exception('Was trying to rename to already '
206 raise Exception('Was trying to rename to already '
207 'existing dir %s' % new_path)
207 'existing dir %s' % new_path)
208 shutil.move(old_path, new_path)
208 shutil.move(old_path, new_path)
209
209
210 def _delete_filesystem_group(self, group, force_delete=False):
210 def _delete_filesystem_group(self, group, force_delete=False):
211 """
211 """
212 Deletes a group from a filesystem
212 Deletes a group from a filesystem
213
213
214 :param group: instance of group from database
214 :param group: instance of group from database
215 :param force_delete: use shutil rmtree to remove all objects
215 :param force_delete: use shutil rmtree to remove all objects
216 """
216 """
217 paths = group.full_path.split(RepoGroup.url_sep())
217 paths = group.full_path.split(RepoGroup.url_sep())
218 paths = os.sep.join(paths)
218 paths = os.sep.join(paths)
219
219
220 rm_path = os.path.join(self.repos_path, paths)
220 rm_path = os.path.join(self.repos_path, paths)
221 log.info("Removing group %s", rm_path)
221 log.info("Removing group %s", rm_path)
222 # delete only if that path really exists
222 # delete only if that path really exists
223 if os.path.isdir(rm_path):
223 if os.path.isdir(rm_path):
224 if force_delete:
224 if force_delete:
225 shutil.rmtree(rm_path)
225 shutil.rmtree(rm_path)
226 else:
226 else:
227 # archive that group`
227 # archive that group`
228 _now = datetime.datetime.now()
228 _now = datetime.datetime.now()
229 _ms = str(_now.microsecond).rjust(6, '0')
229 _ms = str(_now.microsecond).rjust(6, '0')
230 _d = 'rm__%s_GROUP_%s' % (
230 _d = 'rm__%s_GROUP_%s' % (
231 _now.strftime('%Y%m%d_%H%M%S_' + _ms), group.name)
231 _now.strftime('%Y%m%d_%H%M%S_' + _ms), group.name)
232 shutil.move(rm_path, os.path.join(self.repos_path, _d))
232 shutil.move(rm_path, os.path.join(self.repos_path, _d))
233
233
234 def create(self, group_name, group_description, owner, just_db=False,
234 def create(self, group_name, group_description, owner, just_db=False,
235 copy_permissions=False, personal=None, commit_early=True):
235 copy_permissions=False, personal=None, commit_early=True):
236
236
237 (group_name_cleaned,
237 (group_name_cleaned,
238 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(group_name)
238 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(group_name)
239
239
240 parent_group = None
240 parent_group = None
241 if parent_group_name:
241 if parent_group_name:
242 parent_group = self._get_repo_group(parent_group_name)
242 parent_group = self._get_repo_group(parent_group_name)
243 if not parent_group:
243 if not parent_group:
244 # we tried to create a nested group, but the parent is not
244 # we tried to create a nested group, but the parent is not
245 # existing
245 # existing
246 raise ValueError(
246 raise ValueError(
247 'Parent group `%s` given in `%s` group name '
247 'Parent group `%s` given in `%s` group name '
248 'is not yet existing.' % (parent_group_name, group_name))
248 'is not yet existing.' % (parent_group_name, group_name))
249
249
250 # because we are doing a cleanup, we need to check if such directory
250 # because we are doing a cleanup, we need to check if such directory
251 # already exists. If we don't do that we can accidentally delete
251 # already exists. If we don't do that we can accidentally delete
252 # existing directory via cleanup that can cause data issues, since
252 # existing directory via cleanup that can cause data issues, since
253 # delete does a folder rename to special syntax later cleanup
253 # delete does a folder rename to special syntax later cleanup
254 # functions can delete this
254 # functions can delete this
255 cleanup_group = self.check_exist_filesystem(group_name,
255 cleanup_group = self.check_exist_filesystem(group_name,
256 exc_on_failure=False)
256 exc_on_failure=False)
257 user = self._get_user(owner)
257 user = self._get_user(owner)
258 if not user:
258 if not user:
259 raise ValueError('Owner %s not found as rhodecode user', owner)
259 raise ValueError('Owner %s not found as rhodecode user', owner)
260
260
261 try:
261 try:
262 new_repo_group = RepoGroup()
262 new_repo_group = RepoGroup()
263 new_repo_group.user = user
263 new_repo_group.user = user
264 new_repo_group.group_description = group_description or group_name
264 new_repo_group.group_description = group_description or group_name
265 new_repo_group.parent_group = parent_group
265 new_repo_group.parent_group = parent_group
266 new_repo_group.group_name = group_name
266 new_repo_group.group_name = group_name
267 new_repo_group.personal = personal
267 new_repo_group.personal = personal
268
268
269 self.sa.add(new_repo_group)
269 self.sa.add(new_repo_group)
270
270
271 # create an ADMIN permission for owner except if we're super admin,
271 # create an ADMIN permission for owner except if we're super admin,
272 # later owner should go into the owner field of groups
272 # later owner should go into the owner field of groups
273 if not user.is_admin:
273 if not user.is_admin:
274 self.grant_user_permission(repo_group=new_repo_group,
274 self.grant_user_permission(repo_group=new_repo_group,
275 user=owner, perm='group.admin')
275 user=owner, perm='group.admin')
276
276
277 if parent_group and copy_permissions:
277 if parent_group and copy_permissions:
278 # copy permissions from parent
278 # copy permissions from parent
279 user_perms = UserRepoGroupToPerm.query() \
279 user_perms = UserRepoGroupToPerm.query() \
280 .filter(UserRepoGroupToPerm.group == parent_group).all()
280 .filter(UserRepoGroupToPerm.group == parent_group).all()
281
281
282 group_perms = UserGroupRepoGroupToPerm.query() \
282 group_perms = UserGroupRepoGroupToPerm.query() \
283 .filter(UserGroupRepoGroupToPerm.group == parent_group).all()
283 .filter(UserGroupRepoGroupToPerm.group == parent_group).all()
284
284
285 for perm in user_perms:
285 for perm in user_perms:
286 # don't copy over the permission for user who is creating
286 # don't copy over the permission for user who is creating
287 # this group, if he is not super admin he get's admin
287 # this group, if he is not super admin he get's admin
288 # permission set above
288 # permission set above
289 if perm.user != user or user.is_admin:
289 if perm.user != user or user.is_admin:
290 UserRepoGroupToPerm.create(
290 UserRepoGroupToPerm.create(
291 perm.user, new_repo_group, perm.permission)
291 perm.user, new_repo_group, perm.permission)
292
292
293 for perm in group_perms:
293 for perm in group_perms:
294 UserGroupRepoGroupToPerm.create(
294 UserGroupRepoGroupToPerm.create(
295 perm.users_group, new_repo_group, perm.permission)
295 perm.users_group, new_repo_group, perm.permission)
296 else:
296 else:
297 perm_obj = self._create_default_perms(new_repo_group)
297 perm_obj = self._create_default_perms(new_repo_group)
298 self.sa.add(perm_obj)
298 self.sa.add(perm_obj)
299
299
300 # now commit the changes, earlier so we are sure everything is in
300 # now commit the changes, earlier so we are sure everything is in
301 # the database.
301 # the database.
302 if commit_early:
302 if commit_early:
303 self.sa.commit()
303 self.sa.commit()
304 if not just_db:
304 if not just_db:
305 self._create_group(new_repo_group.group_name)
305 self._create_group(new_repo_group.group_name)
306
306
307 # trigger the post hook
307 # trigger the post hook
308 from rhodecode.lib.hooks_base import log_create_repository_group
308 from rhodecode.lib.hooks_base import log_create_repository_group
309 repo_group = RepoGroup.get_by_group_name(group_name)
309 repo_group = RepoGroup.get_by_group_name(group_name)
310 log_create_repository_group(
310 log_create_repository_group(
311 created_by=user.username, **repo_group.get_dict())
311 created_by=user.username, **repo_group.get_dict())
312
312
313 # Trigger create event.
313 # Trigger create event.
314 events.trigger(events.RepoGroupCreateEvent(repo_group))
314 events.trigger(events.RepoGroupCreateEvent(repo_group))
315
315
316 return new_repo_group
316 return new_repo_group
317 except Exception:
317 except Exception:
318 self.sa.rollback()
318 self.sa.rollback()
319 log.exception('Exception occurred when creating repository group, '
319 log.exception('Exception occurred when creating repository group, '
320 'doing cleanup...')
320 'doing cleanup...')
321 # rollback things manually !
321 # rollback things manually !
322 repo_group = RepoGroup.get_by_group_name(group_name)
322 repo_group = RepoGroup.get_by_group_name(group_name)
323 if repo_group:
323 if repo_group:
324 RepoGroup.delete(repo_group.group_id)
324 RepoGroup.delete(repo_group.group_id)
325 self.sa.commit()
325 self.sa.commit()
326 if cleanup_group:
326 if cleanup_group:
327 RepoGroupModel()._delete_filesystem_group(repo_group)
327 RepoGroupModel()._delete_filesystem_group(repo_group)
328 raise
328 raise
329
329
330 def update_permissions(
330 def update_permissions(
331 self, repo_group, perm_additions=None, perm_updates=None,
331 self, repo_group, perm_additions=None, perm_updates=None,
332 perm_deletions=None, recursive=None, check_perms=True,
332 perm_deletions=None, recursive=None, check_perms=True,
333 cur_user=None):
333 cur_user=None):
334 from rhodecode.model.repo import RepoModel
334 from rhodecode.model.repo import RepoModel
335 from rhodecode.lib.auth import HasUserGroupPermissionAny
335 from rhodecode.lib.auth import HasUserGroupPermissionAny
336
336
337 if not perm_additions:
337 if not perm_additions:
338 perm_additions = []
338 perm_additions = []
339 if not perm_updates:
339 if not perm_updates:
340 perm_updates = []
340 perm_updates = []
341 if not perm_deletions:
341 if not perm_deletions:
342 perm_deletions = []
342 perm_deletions = []
343
343
344 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
344 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
345
345
346 changes = {
346 changes = {
347 'added': [],
347 'added': [],
348 'updated': [],
348 'updated': [],
349 'deleted': []
349 'deleted': []
350 }
350 }
351
351
352 def _set_perm_user(obj, user, perm):
352 def _set_perm_user(obj, user, perm):
353 if isinstance(obj, RepoGroup):
353 if isinstance(obj, RepoGroup):
354 self.grant_user_permission(
354 self.grant_user_permission(
355 repo_group=obj, user=user, perm=perm)
355 repo_group=obj, user=user, perm=perm)
356 elif isinstance(obj, Repository):
356 elif isinstance(obj, Repository):
357 # private repos will not allow to change the default
357 # private repos will not allow to change the default
358 # permissions using recursive mode
358 # permissions using recursive mode
359 if obj.private and user == User.DEFAULT_USER:
359 if obj.private and user == User.DEFAULT_USER:
360 return
360 return
361
361
362 # we set group permission but we have to switch to repo
362 # we set group permission but we have to switch to repo
363 # permission
363 # permission
364 perm = perm.replace('group.', 'repository.')
364 perm = perm.replace('group.', 'repository.')
365 RepoModel().grant_user_permission(
365 RepoModel().grant_user_permission(
366 repo=obj, user=user, perm=perm)
366 repo=obj, user=user, perm=perm)
367
367
368 def _set_perm_group(obj, users_group, perm):
368 def _set_perm_group(obj, users_group, perm):
369 if isinstance(obj, RepoGroup):
369 if isinstance(obj, RepoGroup):
370 self.grant_user_group_permission(
370 self.grant_user_group_permission(
371 repo_group=obj, group_name=users_group, perm=perm)
371 repo_group=obj, group_name=users_group, perm=perm)
372 elif isinstance(obj, Repository):
372 elif isinstance(obj, Repository):
373 # we set group permission but we have to switch to repo
373 # we set group permission but we have to switch to repo
374 # permission
374 # permission
375 perm = perm.replace('group.', 'repository.')
375 perm = perm.replace('group.', 'repository.')
376 RepoModel().grant_user_group_permission(
376 RepoModel().grant_user_group_permission(
377 repo=obj, group_name=users_group, perm=perm)
377 repo=obj, group_name=users_group, perm=perm)
378
378
379 def _revoke_perm_user(obj, user):
379 def _revoke_perm_user(obj, user):
380 if isinstance(obj, RepoGroup):
380 if isinstance(obj, RepoGroup):
381 self.revoke_user_permission(repo_group=obj, user=user)
381 self.revoke_user_permission(repo_group=obj, user=user)
382 elif isinstance(obj, Repository):
382 elif isinstance(obj, Repository):
383 RepoModel().revoke_user_permission(repo=obj, user=user)
383 RepoModel().revoke_user_permission(repo=obj, user=user)
384
384
385 def _revoke_perm_group(obj, user_group):
385 def _revoke_perm_group(obj, user_group):
386 if isinstance(obj, RepoGroup):
386 if isinstance(obj, RepoGroup):
387 self.revoke_user_group_permission(
387 self.revoke_user_group_permission(
388 repo_group=obj, group_name=user_group)
388 repo_group=obj, group_name=user_group)
389 elif isinstance(obj, Repository):
389 elif isinstance(obj, Repository):
390 RepoModel().revoke_user_group_permission(
390 RepoModel().revoke_user_group_permission(
391 repo=obj, group_name=user_group)
391 repo=obj, group_name=user_group)
392
392
393 # start updates
393 # start updates
394 log.debug('Now updating permissions for %s in recursive mode:%s',
394 log.debug('Now updating permissions for %s in recursive mode:%s',
395 repo_group, recursive)
395 repo_group, recursive)
396
396
397 # initialize check function, we'll call that multiple times
397 # initialize check function, we'll call that multiple times
398 has_group_perm = HasUserGroupPermissionAny(*req_perms)
398 has_group_perm = HasUserGroupPermissionAny(*req_perms)
399
399
400 for obj in repo_group.recursive_groups_and_repos():
400 for obj in repo_group.recursive_groups_and_repos():
401 # iterated obj is an instance of a repos group or repository in
401 # iterated obj is an instance of a repos group or repository in
402 # that group, recursive option can be: none, repos, groups, all
402 # that group, recursive option can be: none, repos, groups, all
403 if recursive == 'all':
403 if recursive == 'all':
404 obj = obj
404 obj = obj
405 elif recursive == 'repos':
405 elif recursive == 'repos':
406 # skip groups, other than this one
406 # skip groups, other than this one
407 if isinstance(obj, RepoGroup) and not obj == repo_group:
407 if isinstance(obj, RepoGroup) and not obj == repo_group:
408 continue
408 continue
409 elif recursive == 'groups':
409 elif recursive == 'groups':
410 # skip repos
410 # skip repos
411 if isinstance(obj, Repository):
411 if isinstance(obj, Repository):
412 continue
412 continue
413 else: # recursive == 'none':
413 else: # recursive == 'none':
414 # DEFAULT option - don't apply to iterated objects
414 # DEFAULT option - don't apply to iterated objects
415 # also we do a break at the end of this loop. if we are not
415 # also we do a break at the end of this loop. if we are not
416 # in recursive mode
416 # in recursive mode
417 obj = repo_group
417 obj = repo_group
418
418
419 change_obj = obj.get_api_data()
419 change_obj = obj.get_api_data()
420
420
421 # update permissions
421 # update permissions
422 for member_id, perm, member_type in perm_updates:
422 for member_id, perm, member_type in perm_updates:
423 member_id = int(member_id)
423 member_id = int(member_id)
424 if member_type == 'user':
424 if member_type == 'user':
425 member_name = User.get(member_id).username
425 member_name = User.get(member_id).username
426 # this updates also current one if found
426 # this updates also current one if found
427 _set_perm_user(obj, user=member_id, perm=perm)
427 _set_perm_user(obj, user=member_id, perm=perm)
428 elif member_type == 'user_group':
428 elif member_type == 'user_group':
429 member_name = UserGroup.get(member_id).users_group_name
429 member_name = UserGroup.get(member_id).users_group_name
430 if not check_perms or has_group_perm(member_name,
430 if not check_perms or has_group_perm(member_name,
431 user=cur_user):
431 user=cur_user):
432 _set_perm_group(obj, users_group=member_id, perm=perm)
432 _set_perm_group(obj, users_group=member_id, perm=perm)
433 else:
433 else:
434 raise ValueError("member_type must be 'user' or 'user_group' "
434 raise ValueError("member_type must be 'user' or 'user_group' "
435 "got {} instead".format(member_type))
435 "got {} instead".format(member_type))
436
436
437 changes['updated'].append(
437 changes['updated'].append(
438 {'change_obj': change_obj, 'type': member_type,
438 {'change_obj': change_obj, 'type': member_type,
439 'id': member_id, 'name': member_name, 'new_perm': perm})
439 'id': member_id, 'name': member_name, 'new_perm': perm})
440
440
441 # set new permissions
441 # set new permissions
442 for member_id, perm, member_type in perm_additions:
442 for member_id, perm, member_type in perm_additions:
443 member_id = int(member_id)
443 member_id = int(member_id)
444 if member_type == 'user':
444 if member_type == 'user':
445 member_name = User.get(member_id).username
445 member_name = User.get(member_id).username
446 _set_perm_user(obj, user=member_id, perm=perm)
446 _set_perm_user(obj, user=member_id, perm=perm)
447 elif member_type == 'user_group':
447 elif member_type == 'user_group':
448 # check if we have permissions to alter this usergroup
448 # check if we have permissions to alter this usergroup
449 member_name = UserGroup.get(member_id).users_group_name
449 member_name = UserGroup.get(member_id).users_group_name
450 if not check_perms or has_group_perm(member_name,
450 if not check_perms or has_group_perm(member_name,
451 user=cur_user):
451 user=cur_user):
452 _set_perm_group(obj, users_group=member_id, perm=perm)
452 _set_perm_group(obj, users_group=member_id, perm=perm)
453 else:
453 else:
454 raise ValueError("member_type must be 'user' or 'user_group' "
454 raise ValueError("member_type must be 'user' or 'user_group' "
455 "got {} instead".format(member_type))
455 "got {} instead".format(member_type))
456
456
457 changes['added'].append(
457 changes['added'].append(
458 {'change_obj': change_obj, 'type': member_type,
458 {'change_obj': change_obj, 'type': member_type,
459 'id': member_id, 'name': member_name, 'new_perm': perm})
459 'id': member_id, 'name': member_name, 'new_perm': perm})
460
460
461 # delete permissions
461 # delete permissions
462 for member_id, perm, member_type in perm_deletions:
462 for member_id, perm, member_type in perm_deletions:
463 member_id = int(member_id)
463 member_id = int(member_id)
464 if member_type == 'user':
464 if member_type == 'user':
465 member_name = User.get(member_id).username
465 member_name = User.get(member_id).username
466 _revoke_perm_user(obj, user=member_id)
466 _revoke_perm_user(obj, user=member_id)
467 elif member_type == 'user_group':
467 elif member_type == 'user_group':
468 # check if we have permissions to alter this usergroup
468 # check if we have permissions to alter this usergroup
469 member_name = UserGroup.get(member_id).users_group_name
469 member_name = UserGroup.get(member_id).users_group_name
470 if not check_perms or has_group_perm(member_name,
470 if not check_perms or has_group_perm(member_name,
471 user=cur_user):
471 user=cur_user):
472 _revoke_perm_group(obj, user_group=member_id)
472 _revoke_perm_group(obj, user_group=member_id)
473 else:
473 else:
474 raise ValueError("member_type must be 'user' or 'user_group' "
474 raise ValueError("member_type must be 'user' or 'user_group' "
475 "got {} instead".format(member_type))
475 "got {} instead".format(member_type))
476
476
477 changes['deleted'].append(
477 changes['deleted'].append(
478 {'change_obj': change_obj, 'type': member_type,
478 {'change_obj': change_obj, 'type': member_type,
479 'id': member_id, 'name': member_name, 'new_perm': perm})
479 'id': member_id, 'name': member_name, 'new_perm': perm})
480
480
481 # if it's not recursive call for all,repos,groups
481 # if it's not recursive call for all,repos,groups
482 # break the loop and don't proceed with other changes
482 # break the loop and don't proceed with other changes
483 if recursive not in ['all', 'repos', 'groups']:
483 if recursive not in ['all', 'repos', 'groups']:
484 break
484 break
485
485
486 return changes
486 return changes
487
487
488 def update(self, repo_group, form_data):
488 def update(self, repo_group, form_data):
489 try:
489 try:
490 repo_group = self._get_repo_group(repo_group)
490 repo_group = self._get_repo_group(repo_group)
491 old_path = repo_group.full_path
491 old_path = repo_group.full_path
492
492
493 # change properties
493 # change properties
494 if 'group_description' in form_data:
494 if 'group_description' in form_data:
495 repo_group.group_description = form_data['group_description']
495 repo_group.group_description = form_data['group_description']
496
496
497 if 'enable_locking' in form_data:
497 if 'enable_locking' in form_data:
498 repo_group.enable_locking = form_data['enable_locking']
498 repo_group.enable_locking = form_data['enable_locking']
499
499
500 if 'group_parent_id' in form_data:
500 if 'group_parent_id' in form_data:
501 parent_group = (
501 parent_group = (
502 self._get_repo_group(form_data['group_parent_id']))
502 self._get_repo_group(form_data['group_parent_id']))
503 repo_group.group_parent_id = (
503 repo_group.group_parent_id = (
504 parent_group.group_id if parent_group else None)
504 parent_group.group_id if parent_group else None)
505 repo_group.parent_group = parent_group
505 repo_group.parent_group = parent_group
506
506
507 # mikhail: to update the full_path, we have to explicitly
507 # mikhail: to update the full_path, we have to explicitly
508 # update group_name
508 # update group_name
509 group_name = form_data.get('group_name', repo_group.name)
509 group_name = form_data.get('group_name', repo_group.name)
510 repo_group.group_name = repo_group.get_new_name(group_name)
510 repo_group.group_name = repo_group.get_new_name(group_name)
511
511
512 new_path = repo_group.full_path
512 new_path = repo_group.full_path
513
513
514 if 'user' in form_data:
514 if 'user' in form_data:
515 repo_group.user = User.get_by_username(form_data['user'])
515 repo_group.user = User.get_by_username(form_data['user'])
516 repo_group.updated_on = datetime.datetime.now()
516 repo_group.updated_on = datetime.datetime.now()
517 self.sa.add(repo_group)
517 self.sa.add(repo_group)
518
518
519 # iterate over all members of this groups and do fixes
519 # iterate over all members of this groups and do fixes
520 # set locking if given
520 # set locking if given
521 # if obj is a repoGroup also fix the name of the group according
521 # if obj is a repoGroup also fix the name of the group according
522 # to the parent
522 # to the parent
523 # if obj is a Repo fix it's name
523 # if obj is a Repo fix it's name
524 # this can be potentially heavy operation
524 # this can be potentially heavy operation
525 for obj in repo_group.recursive_groups_and_repos():
525 for obj in repo_group.recursive_groups_and_repos():
526 # set the value from it's parent
526 # set the value from it's parent
527 obj.enable_locking = repo_group.enable_locking
527 obj.enable_locking = repo_group.enable_locking
528 if isinstance(obj, RepoGroup):
528 if isinstance(obj, RepoGroup):
529 new_name = obj.get_new_name(obj.name)
529 new_name = obj.get_new_name(obj.name)
530 log.debug('Fixing group %s to new name %s',
530 log.debug('Fixing group %s to new name %s',
531 obj.group_name, new_name)
531 obj.group_name, new_name)
532 obj.group_name = new_name
532 obj.group_name = new_name
533 obj.updated_on = datetime.datetime.now()
533 obj.updated_on = datetime.datetime.now()
534 elif isinstance(obj, Repository):
534 elif isinstance(obj, Repository):
535 # we need to get all repositories from this new group and
535 # we need to get all repositories from this new group and
536 # rename them accordingly to new group path
536 # rename them accordingly to new group path
537 new_name = obj.get_new_name(obj.just_name)
537 new_name = obj.get_new_name(obj.just_name)
538 log.debug('Fixing repo %s to new name %s',
538 log.debug('Fixing repo %s to new name %s',
539 obj.repo_name, new_name)
539 obj.repo_name, new_name)
540 obj.repo_name = new_name
540 obj.repo_name = new_name
541 obj.updated_on = datetime.datetime.now()
541 obj.updated_on = datetime.datetime.now()
542 self.sa.add(obj)
542 self.sa.add(obj)
543
543
544 self._rename_group(old_path, new_path)
544 self._rename_group(old_path, new_path)
545
545
546 # Trigger update event.
546 # Trigger update event.
547 events.trigger(events.RepoGroupUpdateEvent(repo_group))
547 events.trigger(events.RepoGroupUpdateEvent(repo_group))
548
548
549 return repo_group
549 return repo_group
550 except Exception:
550 except Exception:
551 log.error(traceback.format_exc())
551 log.error(traceback.format_exc())
552 raise
552 raise
553
553
554 def delete(self, repo_group, force_delete=False, fs_remove=True):
554 def delete(self, repo_group, force_delete=False, fs_remove=True):
555 repo_group = self._get_repo_group(repo_group)
555 repo_group = self._get_repo_group(repo_group)
556 if not repo_group:
556 if not repo_group:
557 return False
557 return False
558 try:
558 try:
559 self.sa.delete(repo_group)
559 self.sa.delete(repo_group)
560 if fs_remove:
560 if fs_remove:
561 self._delete_filesystem_group(repo_group, force_delete)
561 self._delete_filesystem_group(repo_group, force_delete)
562 else:
562 else:
563 log.debug('skipping removal from filesystem')
563 log.debug('skipping removal from filesystem')
564
564
565 # Trigger delete event.
565 # Trigger delete event.
566 events.trigger(events.RepoGroupDeleteEvent(repo_group))
566 events.trigger(events.RepoGroupDeleteEvent(repo_group))
567 return True
567 return True
568
568
569 except Exception:
569 except Exception:
570 log.error('Error removing repo_group %s', repo_group)
570 log.error('Error removing repo_group %s', repo_group)
571 raise
571 raise
572
572
573 def grant_user_permission(self, repo_group, user, perm):
573 def grant_user_permission(self, repo_group, user, perm):
574 """
574 """
575 Grant permission for user on given repository group, or update
575 Grant permission for user on given repository group, or update
576 existing one if found
576 existing one if found
577
577
578 :param repo_group: Instance of RepoGroup, repositories_group_id,
578 :param repo_group: Instance of RepoGroup, repositories_group_id,
579 or repositories_group name
579 or repositories_group name
580 :param user: Instance of User, user_id or username
580 :param user: Instance of User, user_id or username
581 :param perm: Instance of Permission, or permission_name
581 :param perm: Instance of Permission, or permission_name
582 """
582 """
583
583
584 repo_group = self._get_repo_group(repo_group)
584 repo_group = self._get_repo_group(repo_group)
585 user = self._get_user(user)
585 user = self._get_user(user)
586 permission = self._get_perm(perm)
586 permission = self._get_perm(perm)
587
587
588 # check if we have that permission already
588 # check if we have that permission already
589 obj = self.sa.query(UserRepoGroupToPerm)\
589 obj = self.sa.query(UserRepoGroupToPerm)\
590 .filter(UserRepoGroupToPerm.user == user)\
590 .filter(UserRepoGroupToPerm.user == user)\
591 .filter(UserRepoGroupToPerm.group == repo_group)\
591 .filter(UserRepoGroupToPerm.group == repo_group)\
592 .scalar()
592 .scalar()
593 if obj is None:
593 if obj is None:
594 # create new !
594 # create new !
595 obj = UserRepoGroupToPerm()
595 obj = UserRepoGroupToPerm()
596 obj.group = repo_group
596 obj.group = repo_group
597 obj.user = user
597 obj.user = user
598 obj.permission = permission
598 obj.permission = permission
599 self.sa.add(obj)
599 self.sa.add(obj)
600 log.debug('Granted perm %s to %s on %s', perm, user, repo_group)
600 log.debug('Granted perm %s to %s on %s', perm, user, repo_group)
601 action_logger_generic(
601 action_logger_generic(
602 'granted permission: {} to user: {} on repogroup: {}'.format(
602 'granted permission: {} to user: {} on repogroup: {}'.format(
603 perm, user, repo_group), namespace='security.repogroup')
603 perm, user, repo_group), namespace='security.repogroup')
604 return obj
604 return obj
605
605
606 def revoke_user_permission(self, repo_group, user):
606 def revoke_user_permission(self, repo_group, user):
607 """
607 """
608 Revoke permission for user on given repository group
608 Revoke permission for user on given repository group
609
609
610 :param repo_group: Instance of RepoGroup, repositories_group_id,
610 :param repo_group: Instance of RepoGroup, repositories_group_id,
611 or repositories_group name
611 or repositories_group name
612 :param user: Instance of User, user_id or username
612 :param user: Instance of User, user_id or username
613 """
613 """
614
614
615 repo_group = self._get_repo_group(repo_group)
615 repo_group = self._get_repo_group(repo_group)
616 user = self._get_user(user)
616 user = self._get_user(user)
617
617
618 obj = self.sa.query(UserRepoGroupToPerm)\
618 obj = self.sa.query(UserRepoGroupToPerm)\
619 .filter(UserRepoGroupToPerm.user == user)\
619 .filter(UserRepoGroupToPerm.user == user)\
620 .filter(UserRepoGroupToPerm.group == repo_group)\
620 .filter(UserRepoGroupToPerm.group == repo_group)\
621 .scalar()
621 .scalar()
622 if obj:
622 if obj:
623 self.sa.delete(obj)
623 self.sa.delete(obj)
624 log.debug('Revoked perm on %s on %s', repo_group, user)
624 log.debug('Revoked perm on %s on %s', repo_group, user)
625 action_logger_generic(
625 action_logger_generic(
626 'revoked permission from user: {} on repogroup: {}'.format(
626 'revoked permission from user: {} on repogroup: {}'.format(
627 user, repo_group), namespace='security.repogroup')
627 user, repo_group), namespace='security.repogroup')
628
628
629 def grant_user_group_permission(self, repo_group, group_name, perm):
629 def grant_user_group_permission(self, repo_group, group_name, perm):
630 """
630 """
631 Grant permission for user group on given repository group, or update
631 Grant permission for user group on given repository group, or update
632 existing one if found
632 existing one if found
633
633
634 :param repo_group: Instance of RepoGroup, repositories_group_id,
634 :param repo_group: Instance of RepoGroup, repositories_group_id,
635 or repositories_group name
635 or repositories_group name
636 :param group_name: Instance of UserGroup, users_group_id,
636 :param group_name: Instance of UserGroup, users_group_id,
637 or user group name
637 or user group name
638 :param perm: Instance of Permission, or permission_name
638 :param perm: Instance of Permission, or permission_name
639 """
639 """
640 repo_group = self._get_repo_group(repo_group)
640 repo_group = self._get_repo_group(repo_group)
641 group_name = self._get_user_group(group_name)
641 group_name = self._get_user_group(group_name)
642 permission = self._get_perm(perm)
642 permission = self._get_perm(perm)
643
643
644 # check if we have that permission already
644 # check if we have that permission already
645 obj = self.sa.query(UserGroupRepoGroupToPerm)\
645 obj = self.sa.query(UserGroupRepoGroupToPerm)\
646 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
646 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
647 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
647 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
648 .scalar()
648 .scalar()
649
649
650 if obj is None:
650 if obj is None:
651 # create new
651 # create new
652 obj = UserGroupRepoGroupToPerm()
652 obj = UserGroupRepoGroupToPerm()
653
653
654 obj.group = repo_group
654 obj.group = repo_group
655 obj.users_group = group_name
655 obj.users_group = group_name
656 obj.permission = permission
656 obj.permission = permission
657 self.sa.add(obj)
657 self.sa.add(obj)
658 log.debug('Granted perm %s to %s on %s', perm, group_name, repo_group)
658 log.debug('Granted perm %s to %s on %s', perm, group_name, repo_group)
659 action_logger_generic(
659 action_logger_generic(
660 'granted permission: {} to usergroup: {} on repogroup: {}'.format(
660 'granted permission: {} to usergroup: {} on repogroup: {}'.format(
661 perm, group_name, repo_group), namespace='security.repogroup')
661 perm, group_name, repo_group), namespace='security.repogroup')
662 return obj
662 return obj
663
663
664 def revoke_user_group_permission(self, repo_group, group_name):
664 def revoke_user_group_permission(self, repo_group, group_name):
665 """
665 """
666 Revoke permission for user group on given repository group
666 Revoke permission for user group on given repository group
667
667
668 :param repo_group: Instance of RepoGroup, repositories_group_id,
668 :param repo_group: Instance of RepoGroup, repositories_group_id,
669 or repositories_group name
669 or repositories_group name
670 :param group_name: Instance of UserGroup, users_group_id,
670 :param group_name: Instance of UserGroup, users_group_id,
671 or user group name
671 or user group name
672 """
672 """
673 repo_group = self._get_repo_group(repo_group)
673 repo_group = self._get_repo_group(repo_group)
674 group_name = self._get_user_group(group_name)
674 group_name = self._get_user_group(group_name)
675
675
676 obj = self.sa.query(UserGroupRepoGroupToPerm)\
676 obj = self.sa.query(UserGroupRepoGroupToPerm)\
677 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
677 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
678 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
678 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
679 .scalar()
679 .scalar()
680 if obj:
680 if obj:
681 self.sa.delete(obj)
681 self.sa.delete(obj)
682 log.debug('Revoked perm to %s on %s', repo_group, group_name)
682 log.debug('Revoked perm to %s on %s', repo_group, group_name)
683 action_logger_generic(
683 action_logger_generic(
684 'revoked permission from usergroup: {} on repogroup: {}'.format(
684 'revoked permission from usergroup: {} on repogroup: {}'.format(
685 group_name, repo_group), namespace='security.repogroup')
685 group_name, repo_group), namespace='security.repogroup')
686
686
687 def get_repo_groups_as_dict(self, repo_group_list=None, admin=False,
687 def get_repo_groups_as_dict(self, repo_group_list=None, admin=False,
688 super_user_actions=False):
688 super_user_actions=False):
689
689
690 from pyramid.threadlocal import get_current_request
690 from pyramid.threadlocal import get_current_request
691 _render = get_current_request().get_partial_renderer(
691 _render = get_current_request().get_partial_renderer(
692 'rhodecode:templates/data_table/_dt_elements.mako')
692 'rhodecode:templates/data_table/_dt_elements.mako')
693 c = _render.get_call_context()
693 c = _render.get_call_context()
694 h = _render.get_helpers()
694 h = _render.get_helpers()
695
695
696 def quick_menu(repo_group_name):
696 def quick_menu(repo_group_name):
697 return _render('quick_repo_group_menu', repo_group_name)
697 return _render('quick_repo_group_menu', repo_group_name)
698
698
699 def repo_group_lnk(repo_group_name):
699 def repo_group_lnk(repo_group_name):
700 return _render('repo_group_name', repo_group_name)
700 return _render('repo_group_name', repo_group_name)
701
701
702 def last_change(last_change):
702 def last_change(last_change):
703 if admin and isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
703 if admin and isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
704 last_change = last_change + datetime.timedelta(seconds=
704 last_change = last_change + datetime.timedelta(seconds=
705 (datetime.datetime.now() - datetime.datetime.utcnow()).seconds)
705 (datetime.datetime.now() - datetime.datetime.utcnow()).seconds)
706 return _render("last_change", last_change)
706 return _render("last_change", last_change)
707
707
708 def desc(desc, personal):
708 def desc(desc, personal):
709 return _render(
709 return _render(
710 'repo_group_desc', desc, personal, c.visual.stylify_metatags)
710 'repo_group_desc', desc, personal, c.visual.stylify_metatags)
711
711
712 def repo_group_actions(repo_group_id, repo_group_name, gr_count):
712 def repo_group_actions(repo_group_id, repo_group_name, gr_count):
713 return _render(
713 return _render(
714 'repo_group_actions', repo_group_id, repo_group_name, gr_count)
714 'repo_group_actions', repo_group_id, repo_group_name, gr_count)
715
715
716 def repo_group_name(repo_group_name, children_groups):
716 def repo_group_name(repo_group_name, children_groups):
717 return _render("repo_group_name", repo_group_name, children_groups)
717 return _render("repo_group_name", repo_group_name, children_groups)
718
718
719 def user_profile(username):
719 def user_profile(username):
720 return _render('user_profile', username)
720 return _render('user_profile', username)
721
721
722 repo_group_data = []
722 repo_group_data = []
723 for group in repo_group_list:
723 for group in repo_group_list:
724
724
725 row = {
725 row = {
726 "menu": quick_menu(group.group_name),
726 "menu": quick_menu(group.group_name),
727 "name": repo_group_lnk(group.group_name),
727 "name": repo_group_lnk(group.group_name),
728 "name_raw": group.group_name,
728 "name_raw": group.group_name,
729 "last_change": last_change(group.last_db_change),
729 "last_change": last_change(group.last_db_change),
730 "last_change_raw": datetime_to_time(group.last_db_change),
730 "last_change_raw": datetime_to_time(group.last_db_change),
731
732 "last_changeset": "",
733 "last_changeset_raw": "",
734
731 "desc": desc(group.description_safe, group.personal),
735 "desc": desc(group.description_safe, group.personal),
732 "top_level_repos": 0,
736 "top_level_repos": 0,
733 "owner": user_profile(group.user.username)
737 "owner": user_profile(group.user.username)
734 }
738 }
735 if admin:
739 if admin:
736 repo_count = group.repositories.count()
740 repo_count = group.repositories.count()
737 children_groups = map(
741 children_groups = map(
738 h.safe_unicode,
742 h.safe_unicode,
739 itertools.chain((g.name for g in group.parents),
743 itertools.chain((g.name for g in group.parents),
740 (x.name for x in [group])))
744 (x.name for x in [group])))
741 row.update({
745 row.update({
742 "action": repo_group_actions(
746 "action": repo_group_actions(
743 group.group_id, group.group_name, repo_count),
747 group.group_id, group.group_name, repo_count),
744 "top_level_repos": repo_count,
748 "top_level_repos": repo_count,
745 "name": repo_group_name(group.group_name, children_groups),
749 "name": repo_group_name(group.group_name, children_groups),
746
750
747 })
751 })
748 repo_group_data.append(row)
752 repo_group_data.append(row)
749
753
750 return repo_group_data
754 return repo_group_data
751
755
752 def _get_defaults(self, repo_group_name):
756 def _get_defaults(self, repo_group_name):
753 repo_group = RepoGroup.get_by_group_name(repo_group_name)
757 repo_group = RepoGroup.get_by_group_name(repo_group_name)
754
758
755 if repo_group is None:
759 if repo_group is None:
756 return None
760 return None
757
761
758 defaults = repo_group.get_dict()
762 defaults = repo_group.get_dict()
759 defaults['repo_group_name'] = repo_group.name
763 defaults['repo_group_name'] = repo_group.name
760 defaults['repo_group_description'] = repo_group.group_description
764 defaults['repo_group_description'] = repo_group.group_description
761 defaults['repo_group_enable_locking'] = repo_group.enable_locking
765 defaults['repo_group_enable_locking'] = repo_group.enable_locking
762
766
763 # we use -1 as this is how in HTML, we mark an empty group
767 # we use -1 as this is how in HTML, we mark an empty group
764 defaults['repo_group'] = defaults['group_parent_id'] or -1
768 defaults['repo_group'] = defaults['group_parent_id'] or -1
765
769
766 # fill owner
770 # fill owner
767 if repo_group.user:
771 if repo_group.user:
768 defaults.update({'user': repo_group.user.username})
772 defaults.update({'user': repo_group.user.username})
769 else:
773 else:
770 replacement_user = User.get_first_super_admin().username
774 replacement_user = User.get_first_super_admin().username
771 defaults.update({'user': replacement_user})
775 defaults.update({'user': replacement_user})
772
776
773 return defaults
777 return defaults
@@ -1,509 +1,514 b''
1
1
2 // tables.less
2 // tables.less
3 // For use in RhodeCode application tables;
3 // For use in RhodeCode application tables;
4 // see style guide documentation for guidelines.
4 // see style guide documentation for guidelines.
5
5
6 // TABLES
6 // TABLES
7
7
8 .rctable,
8 .rctable,
9 table.rctable,
9 table.rctable,
10 table.dataTable {
10 table.dataTable {
11 clear:both;
11 clear:both;
12 width: 100%;
12 width: 100%;
13 margin: 0 auto @padding;
13 margin: 0 auto @padding;
14 padding: 0;
14 padding: 0;
15 vertical-align: baseline;
15 vertical-align: baseline;
16 line-height:1.5em;
16 line-height:1.5em;
17 border: none;
17 border: none;
18 outline: none;
18 outline: none;
19 border-collapse: collapse;
19 border-collapse: collapse;
20 border-spacing: 0;
20 border-spacing: 0;
21 color: @grey2;
21 color: @grey2;
22
22
23 b {
23 b {
24 font-weight: normal;
24 font-weight: normal;
25 }
25 }
26
26
27 em {
27 em {
28 font-weight: bold;
28 font-weight: bold;
29 font-style: normal;
29 font-style: normal;
30 }
30 }
31
31
32 th,
32 th,
33 td {
33 td {
34 height: auto;
34 height: auto;
35 max-width: 20%;
35 max-width: 20%;
36 padding: .65em 1em .65em 0;
36 padding: .65em 1em .65em 0;
37 vertical-align: middle;
37 vertical-align: middle;
38 border-bottom: @border-thickness solid @grey5;
38 border-bottom: @border-thickness solid @grey5;
39 white-space: normal;
39 white-space: normal;
40
40
41 &.td-radio,
41 &.td-radio,
42 &.td-checkbox {
42 &.td-checkbox {
43 padding-right: 0;
43 padding-right: 0;
44 text-align: center;
44 text-align: center;
45
45
46 input {
46 input {
47 margin: 0 1em;
47 margin: 0 1em;
48 }
48 }
49 }
49 }
50
50
51 &.truncate-wrap {
51 &.truncate-wrap {
52 white-space: nowrap !important;
52 white-space: nowrap !important;
53 }
53 }
54
54
55 pre {
55 pre {
56 margin: 0;
56 margin: 0;
57 }
57 }
58
58
59 .show_more {
59 .show_more {
60 height: inherit;
60 height: inherit;
61 }
61 }
62 }
62 }
63
63
64 .expired td {
64 .expired td {
65 background-color: @grey7;
65 background-color: @grey7;
66 }
66 }
67 .inactive td {
67 .inactive td {
68 background-color: @grey6;
68 background-color: @grey6;
69 }
69 }
70 th {
70 th {
71 text-align: left;
71 text-align: left;
72 font-weight: @text-semibold-weight;
72 font-weight: @text-semibold-weight;
73 font-family: @text-semibold;
73 font-family: @text-semibold;
74 }
74 }
75
75
76 .hl {
76 .hl {
77 td {
77 td {
78 background-color: lighten(@alert4,25%);
78 background-color: lighten(@alert4,25%);
79 }
79 }
80 }
80 }
81
81
82 // Special Data Cell Types
82 // Special Data Cell Types
83 // See style guide for desciptions and examples.
83 // See style guide for desciptions and examples.
84
84
85 td {
85 td {
86
86
87 &.user {
87 &.user {
88 padding-left: 1em;
88 padding-left: 1em;
89 }
89 }
90
90
91 &.td-rss {
91 &.td-rss {
92 width: 20px;
92 width: 20px;
93 min-width: 0;
93 min-width: 0;
94 margin: 0;
94 margin: 0;
95 }
95 }
96
96
97 &.quick_repo_menu {
97 &.quick_repo_menu {
98 width: 15px;
98 width: 15px;
99 text-align: center;
99 text-align: center;
100
100
101 &:hover {
101 &:hover {
102 background-color: @grey5;
102 background-color: @grey5;
103 }
103 }
104 }
104 }
105
105
106 &.td-hash {
106 &.td-hash {
107 min-width: 80px;
107 min-width: 80px;
108 width: 200px;
108 width: 200px;
109
109
110 .obsolete {
110 .obsolete {
111 text-decoration: line-through;
111 text-decoration: line-through;
112 color: lighten(@grey2,25%);
112 color: lighten(@grey2,25%);
113 }
113 }
114 }
114 }
115
115
116 &.td-time {
116 &.td-time {
117 width: 160px;
117 width: 160px;
118 white-space: nowrap;
118 white-space: nowrap;
119 }
119 }
120
120
121 &.annotate{
121 &.annotate{
122 padding-right: 0;
122 padding-right: 0;
123
123
124 div.annotatediv{
124 div.annotatediv{
125 margin: 0 0.7em;
125 margin: 0 0.7em;
126 }
126 }
127 }
127 }
128
128
129 &.tags-col {
129 &.tags-col {
130 padding-right: 0;
130 padding-right: 0;
131 }
131 }
132
132
133 &.td-description {
133 &.td-description {
134 min-width: 350px;
134 min-width: 350px;
135
135
136 &.truncate, .truncate-wrap {
136 &.truncate, .truncate-wrap {
137 white-space: nowrap;
137 white-space: nowrap;
138 overflow: hidden;
138 overflow: hidden;
139 text-overflow: ellipsis;
139 text-overflow: ellipsis;
140 max-width: 350px;
140 max-width: 350px;
141 }
141 }
142 }
142 }
143
143
144 &.td-grid-name {
145 white-space: nowrap;
146 min-width: 300px;
147 }
148
144 &.td-componentname {
149 &.td-componentname {
145 white-space: nowrap;
150 white-space: nowrap;
146 }
151 }
147
152
148 &.td-name {
153 &.td-name {
149
154
150 }
155 }
151
156
152 &.td-journalaction {
157 &.td-journalaction {
153 min-width: 300px;
158 min-width: 300px;
154
159
155 .journal_action_params {
160 .journal_action_params {
156 // waiting for feedback
161 // waiting for feedback
157 }
162 }
158 }
163 }
159
164
160 &.td-active {
165 &.td-active {
161 padding-left: .65em;
166 padding-left: .65em;
162 }
167 }
163
168
164 &.td-url {
169 &.td-url {
165 white-space: nowrap;
170 white-space: nowrap;
166 }
171 }
167
172
168 &.td-comments {
173 &.td-comments {
169 min-width: 3em;
174 min-width: 3em;
170 }
175 }
171
176
172 &.td-buttons {
177 &.td-buttons {
173 padding: .3em 0;
178 padding: .3em 0;
174 }
179 }
175 &.td-align-top {
180 &.td-align-top {
176 vertical-align: text-top
181 vertical-align: text-top
177 }
182 }
178 &.td-action {
183 &.td-action {
179 // this is for the remove/delete/edit buttons
184 // this is for the remove/delete/edit buttons
180 padding-right: 0;
185 padding-right: 0;
181 min-width: 95px;
186 min-width: 95px;
182 text-transform: capitalize;
187 text-transform: capitalize;
183
188
184 i {
189 i {
185 display: none;
190 display: none;
186 }
191 }
187 }
192 }
188
193
189 // TODO: lisa: this needs to be cleaned up with the buttons
194 // TODO: lisa: this needs to be cleaned up with the buttons
190 .grid_edit,
195 .grid_edit,
191 .grid_delete {
196 .grid_delete {
192 display: inline-block;
197 display: inline-block;
193 margin: 0 @padding/3 0 0;
198 margin: 0 @padding/3 0 0;
194 font-family: @text-light;
199 font-family: @text-light;
195
200
196 i {
201 i {
197 display: none;
202 display: none;
198 }
203 }
199 }
204 }
200
205
201 .grid_edit + .grid_delete {
206 .grid_edit + .grid_delete {
202 border-left: @border-thickness solid @grey5;
207 border-left: @border-thickness solid @grey5;
203 padding-left: @padding/2;
208 padding-left: @padding/2;
204 }
209 }
205
210
206 &.td-compare {
211 &.td-compare {
207
212
208 input {
213 input {
209 margin-right: 1em;
214 margin-right: 1em;
210 }
215 }
211
216
212 .compare-radio-button {
217 .compare-radio-button {
213 margin: 0 1em 0 0;
218 margin: 0 1em 0 0;
214 }
219 }
215
220
216
221
217 }
222 }
218
223
219 &.td-tags {
224 &.td-tags {
220 padding: .5em 1em .5em 0;
225 padding: .5em 1em .5em 0;
221 width: 140px;
226 width: 140px;
222
227
223 .tag {
228 .tag {
224 margin: 1px;
229 margin: 1px;
225 float: left;
230 float: left;
226 }
231 }
227 }
232 }
228
233
229 .icon-svn, .icon-hg, .icon-git {
234 .icon-svn, .icon-hg, .icon-git {
230 font-size: 1.4em;
235 font-size: 1.4em;
231 }
236 }
232
237
233 &.collapse_commit,
238 &.collapse_commit,
234 &.expand_commit {
239 &.expand_commit {
235 padding-right: 0;
240 padding-right: 0;
236 padding-left: 1em;
241 padding-left: 1em;
237 cursor: pointer;
242 cursor: pointer;
238 width: 20px;
243 width: 20px;
239 }
244 }
240 }
245 }
241
246
242 .perm_admin_row {
247 .perm_admin_row {
243 color: @grey4;
248 color: @grey4;
244 background-color: @grey6;
249 background-color: @grey6;
245 }
250 }
246
251
247 .noborder {
252 .noborder {
248 border: none;
253 border: none;
249
254
250 td {
255 td {
251 border: none;
256 border: none;
252 }
257 }
253 }
258 }
254 }
259 }
255 .rctable.audit-log {
260 .rctable.audit-log {
256 td {
261 td {
257 vertical-align: top;
262 vertical-align: top;
258 }
263 }
259 }
264 }
260
265
261 // TRUNCATING
266 // TRUNCATING
262 // TODO: lisaq: should this possibly be moved out of tables.less?
267 // TODO: lisaq: should this possibly be moved out of tables.less?
263 // for truncated text
268 // for truncated text
264 // used inside of table cells and in code block headers
269 // used inside of table cells and in code block headers
265 .truncate-wrap {
270 .truncate-wrap {
266 white-space: nowrap !important;
271 white-space: nowrap !important;
267
272
268 //truncated text
273 //truncated text
269 .truncate {
274 .truncate {
270 max-width: 450px;
275 max-width: 450px;
271 width: 300px;
276 width: 300px;
272 overflow: hidden;
277 overflow: hidden;
273 text-overflow: ellipsis;
278 text-overflow: ellipsis;
274 -o-text-overflow: ellipsis;
279 -o-text-overflow: ellipsis;
275 -ms-text-overflow: ellipsis;
280 -ms-text-overflow: ellipsis;
276
281
277 &.autoexpand {
282 &.autoexpand {
278 width: 120px;
283 width: 120px;
279 margin-right: 200px;
284 margin-right: 200px;
280 }
285 }
281 }
286 }
282 &:hover .truncate.autoexpand {
287 &:hover .truncate.autoexpand {
283 overflow: visible;
288 overflow: visible;
284 }
289 }
285
290
286 .tags-truncate {
291 .tags-truncate {
287 width: 150px;
292 width: 150px;
288 height: 22px;
293 height: 22px;
289 overflow: hidden;
294 overflow: hidden;
290
295
291 .tag {
296 .tag {
292 display: inline-block;
297 display: inline-block;
293 }
298 }
294
299
295 &.truncate {
300 &.truncate {
296 height: 22px;
301 height: 22px;
297 max-height:2em;
302 max-height:2em;
298 width: 140px;
303 width: 140px;
299 }
304 }
300 }
305 }
301 }
306 }
302
307
303 .apikeys_wrap {
308 .apikeys_wrap {
304 margin-bottom: @padding;
309 margin-bottom: @padding;
305
310
306 table.rctable td:first-child {
311 table.rctable td:first-child {
307 width: 340px;
312 width: 340px;
308 }
313 }
309 }
314 }
310
315
311
316
312
317
313 // SPECIAL CASES
318 // SPECIAL CASES
314
319
315 // Repository Followers
320 // Repository Followers
316 table.rctable.followers_data {
321 table.rctable.followers_data {
317 width: 75%;
322 width: 75%;
318 margin: 0;
323 margin: 0;
319 }
324 }
320
325
321 // Repository List
326 // Repository List
322 // Group Members List
327 // Group Members List
323 table.rctable.group_members,
328 table.rctable.group_members,
324 table#repo_list_table {
329 table#repo_list_table {
325 min-width: 600px;
330 min-width: 600px;
326 }
331 }
327
332
328 // Keyboard mappings
333 // Keyboard mappings
329 table.keyboard-mappings {
334 table.keyboard-mappings {
330 th {
335 th {
331 text-align: left;
336 text-align: left;
332 font-weight: @text-semibold-weight;
337 font-weight: @text-semibold-weight;
333 font-family: @text-semibold;
338 font-family: @text-semibold;
334 }
339 }
335 }
340 }
336
341
337 // Branches, Tags, and Bookmarks
342 // Branches, Tags, and Bookmarks
338 #obj_list_table.dataTable {
343 #obj_list_table.dataTable {
339 td.td-time {
344 td.td-time {
340 padding-right: 1em;
345 padding-right: 1em;
341 }
346 }
342 }
347 }
343
348
344 // User Admin
349 // User Admin
345 .rctable.useremails,
350 .rctable.useremails,
346 .rctable.account_emails {
351 .rctable.account_emails {
347 .tag,
352 .tag,
348 .btn {
353 .btn {
349 float: right;
354 float: right;
350 }
355 }
351 .btn { //to line up with tags
356 .btn { //to line up with tags
352 margin-right: 1.65em;
357 margin-right: 1.65em;
353 }
358 }
354 }
359 }
355
360
356 // User List
361 // User List
357 #user_list_table {
362 #user_list_table {
358
363
359 td.td-user {
364 td.td-user {
360 min-width: 100px;
365 min-width: 100px;
361 }
366 }
362 }
367 }
363
368
364 // Pull Request List Table
369 // Pull Request List Table
365 #pull_request_list_table.dataTable {
370 #pull_request_list_table.dataTable {
366
371
367 //TODO: lisa: This needs to be removed once the description is adjusted
372 //TODO: lisa: This needs to be removed once the description is adjusted
368 // for using an expand_commit button (see issue 765)
373 // for using an expand_commit button (see issue 765)
369 td {
374 td {
370 vertical-align: middle;
375 vertical-align: middle;
371 }
376 }
372 }
377 }
373
378
374 // Settings (no border)
379 // Settings (no border)
375 table.rctable.dl-settings {
380 table.rctable.dl-settings {
376 td {
381 td {
377 border: none;
382 border: none;
378 vertical-align: baseline;
383 vertical-align: baseline;
379 }
384 }
380 }
385 }
381
386
382
387
383 // Statistics
388 // Statistics
384 table.trending_language_tbl {
389 table.trending_language_tbl {
385 width: 100%;
390 width: 100%;
386 line-height: 1em;
391 line-height: 1em;
387
392
388 td div {
393 td div {
389 overflow: visible;
394 overflow: visible;
390 }
395 }
391 }
396 }
392
397
393 .trending_language_tbl, .trending_language_tbl td {
398 .trending_language_tbl, .trending_language_tbl td {
394 border: 0;
399 border: 0;
395 margin: 0;
400 margin: 0;
396 padding: 0;
401 padding: 0;
397 background: transparent;
402 background: transparent;
398 }
403 }
399
404
400 .trending_language_tbl, .trending_language_tbl tr {
405 .trending_language_tbl, .trending_language_tbl tr {
401 border-spacing: 0 3px;
406 border-spacing: 0 3px;
402 }
407 }
403
408
404 .trending_language {
409 .trending_language {
405 position: relative;
410 position: relative;
406 width: 100%;
411 width: 100%;
407 height: 19px;
412 height: 19px;
408 overflow: hidden;
413 overflow: hidden;
409 background-color: @grey6;
414 background-color: @grey6;
410
415
411 span, b{
416 span, b{
412 position: absolute;
417 position: absolute;
413 display: block;
418 display: block;
414 height: 12px;
419 height: 12px;
415 margin-bottom: 0px;
420 margin-bottom: 0px;
416 white-space: pre;
421 white-space: pre;
417 padding: floor(@basefontsize/4);
422 padding: floor(@basefontsize/4);
418 top: 0;
423 top: 0;
419 left: 0;
424 left: 0;
420 }
425 }
421
426
422 span{
427 span{
423 color: @text-color;
428 color: @text-color;
424 z-index: 0;
429 z-index: 0;
425 min-width: 20px;
430 min-width: 20px;
426 }
431 }
427
432
428 b {
433 b {
429 z-index: 1;
434 z-index: 1;
430 overflow: hidden;
435 overflow: hidden;
431 background-color: @rcblue;
436 background-color: @rcblue;
432 color: #FFF;
437 color: #FFF;
433 text-decoration: none;
438 text-decoration: none;
434 }
439 }
435
440
436 }
441 }
437
442
438 // Changesets
443 // Changesets
439 #changesets.rctable {
444 #changesets.rctable {
440
445
441 // td must be fixed height for graph
446 // td must be fixed height for graph
442 td {
447 td {
443 height: 32px;
448 height: 32px;
444 padding: 0 1em 0 0;
449 padding: 0 1em 0 0;
445 vertical-align: middle;
450 vertical-align: middle;
446 white-space: nowrap;
451 white-space: nowrap;
447
452
448 &.td-description {
453 &.td-description {
449 white-space: normal;
454 white-space: normal;
450 }
455 }
451
456
452 &.expand_commit {
457 &.expand_commit {
453 padding-right: 0;
458 padding-right: 0;
454 cursor: pointer;
459 cursor: pointer;
455 width: 20px;
460 width: 20px;
456 }
461 }
457 }
462 }
458 }
463 }
459
464
460 // Compare
465 // Compare
461 table.compare_view_commits {
466 table.compare_view_commits {
462 margin-top: @space;
467 margin-top: @space;
463
468
464 td.td-time {
469 td.td-time {
465 padding-left: .5em;
470 padding-left: .5em;
466 }
471 }
467
472
468 // special case to not show hover actions on hidden indicator
473 // special case to not show hover actions on hidden indicator
469 tr.compare_select_hidden:hover {
474 tr.compare_select_hidden:hover {
470 cursor: inherit;
475 cursor: inherit;
471
476
472 td {
477 td {
473 background-color: inherit;
478 background-color: inherit;
474 }
479 }
475 }
480 }
476
481
477 tr:hover {
482 tr:hover {
478 cursor: pointer;
483 cursor: pointer;
479
484
480 td {
485 td {
481 background-color: lighten(@alert4,25%);
486 background-color: lighten(@alert4,25%);
482 }
487 }
483 }
488 }
484
489
485
490
486 }
491 }
487
492
488 .file_history {
493 .file_history {
489 td.td-actions {
494 td.td-actions {
490 text-align: right;
495 text-align: right;
491 }
496 }
492 }
497 }
493
498
494
499
495 // Gist List
500 // Gist List
496 #gist_list_table {
501 #gist_list_table {
497 td {
502 td {
498 vertical-align: middle;
503 vertical-align: middle;
499
504
500 div{
505 div{
501 display: inline-block;
506 display: inline-block;
502 vertical-align: middle;
507 vertical-align: middle;
503 }
508 }
504
509
505 img{
510 img{
506 vertical-align: middle;
511 vertical-align: middle;
507 }
512 }
508 }
513 }
509 }
514 }
@@ -1,138 +1,141 b''
1 <%inherit file="/base/base.mako"/>
1 <%inherit file="/base/base.mako"/>
2
2
3
3
4 <%def name="menu_bar_subnav()">
4 <%def name="menu_bar_subnav()">
5 % if c.repo_group:
5 % if c.repo_group:
6 ${self.repo_group_menu(active='home')}
6 ${self.repo_group_menu(active='home')}
7 % endif
7 % endif
8 </%def>
8 </%def>
9
9
10
10
11 <%def name="main()">
11 <%def name="main()">
12 <div class="box">
12 <div class="box">
13 <!-- box / title -->
13 <!-- box / title -->
14 <div class="title">
14 <div class="title">
15 % if c.repo_group:
15 % if c.repo_group:
16 ${self.repo_group_page_title(c.repo_group)}
16 ${self.repo_group_page_title(c.repo_group)}
17 ## context actions
17 ## context actions
18 <div>
18 <div>
19 <ul class="links icon-only-links block-right">
19 <ul class="links icon-only-links block-right">
20 <li></li>
20 <li></li>
21 </ul>
21 </ul>
22 </div>
22 </div>
23 % endif
23 % endif
24
24
25 %if c.rhodecode_user.username != h.DEFAULT_USER:
25 %if c.rhodecode_user.username != h.DEFAULT_USER:
26 <div class="block-right">
26 <div class="block-right">
27 <%
27 <%
28 is_admin = h.HasPermissionAny('hg.admin')('can create repos index page')
28 is_admin = h.HasPermissionAny('hg.admin')('can create repos index page')
29 create_repo = h.HasPermissionAny('hg.create.repository')('can create repository index page')
29 create_repo = h.HasPermissionAny('hg.create.repository')('can create repository index page')
30 create_repo_group = h.HasPermissionAny('hg.repogroup.create.true')('can create repository groups index page')
30 create_repo_group = h.HasPermissionAny('hg.repogroup.create.true')('can create repository groups index page')
31 create_user_group = h.HasPermissionAny('hg.usergroup.create.true')('can create user groups index page')
31 create_user_group = h.HasPermissionAny('hg.usergroup.create.true')('can create user groups index page')
32 %>
32 %>
33
33
34 %if not c.repo_group:
34 %if not c.repo_group:
35 ## no repository group context here
35 ## no repository group context here
36 %if is_admin or create_repo:
36 %if is_admin or create_repo:
37 <a href="${h.route_path('repo_new')}" class="btn btn-small btn-success btn-primary">${_('Add Repository')}</a>
37 <a href="${h.route_path('repo_new')}" class="btn btn-small btn-success btn-primary">${_('Add Repository')}</a>
38 %endif
38 %endif
39
39
40 %if is_admin or create_repo_group:
40 %if is_admin or create_repo_group:
41 <a href="${h.route_path('repo_group_new')}" class="btn btn-small btn-default">${_(u'Add Repository Group')}</a>
41 <a href="${h.route_path('repo_group_new')}" class="btn btn-small btn-default">${_(u'Add Repository Group')}</a>
42 %endif
42 %endif
43 %endif
43 %endif
44 </div>
44 </div>
45 %endif
45 %endif
46 </div>
46 </div>
47 <!-- end box / title -->
47 <!-- end box / title -->
48 <div class="table">
48 <div class="table">
49 <div id="groups_list_wrap">
49 <div id="groups_list_wrap">
50 <table id="group_list_table" class="display" style="width: 100%"></table>
50 <table id="group_list_table" class="display" style="width: 100%"></table>
51 </div>
51 </div>
52 </div>
52 </div>
53
53
54 <div class="table">
54 <div class="table">
55 <div id="repos_list_wrap">
55 <div id="repos_list_wrap">
56 <table id="repo_list_table" class="display" style="width: 100%"></table>
56 <table id="repo_list_table" class="display" style="width: 100%"></table>
57 </div>
57 </div>
58 </div>
58 </div>
59
59
60 ## no repository groups and repos present, show something to the users
60 ## no repository groups and repos present, show something to the users
61 % if c.repo_groups_data == '[]' and c.repos_data == '[]':
61 % if c.repo_groups_data == '[]' and c.repos_data == '[]':
62 <div class="table">
62 <div class="table">
63 <h2 class="no-object-border">
63 <h2 class="no-object-border">
64 ${_('No repositories or repositories groups exists here.')}
64 ${_('No repositories or repositories groups exists here.')}
65 </h2>
65 </h2>
66 </div>
66 </div>
67 % endif
67 % endif
68
68
69 </div>
69 </div>
70 <script>
70 <script>
71 $(document).ready(function() {
71 $(document).ready(function() {
72
72
73 // repo group list
73 // repo group list
74 % if c.repo_groups_data != '[]':
74 % if c.repo_groups_data != '[]':
75 $('#group_list_table').DataTable({
75 $('#group_list_table').DataTable({
76 data: ${c.repo_groups_data|n},
76 data: ${c.repo_groups_data|n},
77 dom: 'rtp',
77 dom: 'rtp',
78 pageLength: ${c.visual.dashboard_items},
78 pageLength: ${c.visual.dashboard_items},
79 order: [[ 0, "asc" ]],
79 order: [[ 0, "asc" ]],
80 columns: [
80 columns: [
81 { data: {"_": "name",
81 { data: {"_": "name",
82 "sort": "name_raw"}, title: "${_('Name')}", className: "td-componentname" },
82 "sort": "name_raw"}, title: "${_('Name')}", className: "truncate-wrap td-grid-name" },
83 { data: 'menu', "bSortable": false, className: "quick_repo_menu" },
83 { data: 'menu', "bSortable": false, className: "quick_repo_menu" },
84 { data: {"_": "desc",
84 { data: {"_": "desc",
85 "sort": "desc"}, title: "${_('Description')}", className: "td-description" },
85 "sort": "desc"}, title: "${_('Description')}", className: "td-description" },
86 { data: {"_": "last_change",
86 { data: {"_": "last_change",
87 "sort": "last_change_raw",
87 "sort": "last_change_raw",
88 "type": Number}, title: "${_('Last Change')}", className: "td-time" },
88 "type": Number}, title: "${_('Last Change')}", className: "td-time" },
89 { data: {"_": "last_changeset",
90 "sort": "last_changeset_raw",
91 "type": Number}, title: "", className: "td-hash" },
89 { data: {"_": "owner",
92 { data: {"_": "owner",
90 "sort": "owner"}, title: "${_('Owner')}", className: "td-user" }
93 "sort": "owner"}, title: "${_('Owner')}", className: "td-user" }
91 ],
94 ],
92 language: {
95 language: {
93 paginate: DEFAULT_GRID_PAGINATION,
96 paginate: DEFAULT_GRID_PAGINATION,
94 emptyTable: _gettext("No repository groups available yet.")
97 emptyTable: _gettext("No repository groups available yet.")
95 },
98 },
96 "drawCallback": function( settings, json ) {
99 "drawCallback": function( settings, json ) {
97 timeagoActivate();
100 timeagoActivate();
98 quick_repo_menu();
101 quick_repo_menu();
99 }
102 }
100 });
103 });
101 % endif
104 % endif
102
105
103 // repo list
106 // repo list
104 % if c.repos_data != '[]':
107 % if c.repos_data != '[]':
105 $('#repo_list_table').DataTable({
108 $('#repo_list_table').DataTable({
106 data: ${c.repos_data|n},
109 data: ${c.repos_data|n},
107 dom: 'rtp',
110 dom: 'rtp',
108 order: [[ 0, "asc" ]],
111 order: [[ 0, "asc" ]],
109 pageLength: ${c.visual.dashboard_items},
112 pageLength: ${c.visual.dashboard_items},
110 columns: [
113 columns: [
111 { data: {"_": "name",
114 { data: {"_": "name",
112 "sort": "name_raw"}, title: "${_('Name')}", className: "truncate-wrap td-componentname" },
115 "sort": "name_raw"}, title: "${_('Name')}", className: "truncate-wrap td-grid-name" },
113 { data: 'menu', "bSortable": false, className: "quick_repo_menu" },
116 { data: 'menu', "bSortable": false, className: "quick_repo_menu" },
114 { data: {"_": "desc",
117 { data: {"_": "desc",
115 "sort": "desc"}, title: "${_('Description')}", className: "td-description" },
118 "sort": "desc"}, title: "${_('Description')}", className: "td-description" },
116 { data: {"_": "last_change",
119 { data: {"_": "last_change",
117 "sort": "last_change_raw",
120 "sort": "last_change_raw",
118 "type": Number}, title: "${_('Last Change')}", className: "td-time" },
121 "type": Number}, title: "${_('Last Change')}", className: "td-time" },
119 { data: {"_": "last_changeset",
122 { data: {"_": "last_changeset",
120 "sort": "last_changeset_raw",
123 "sort": "last_changeset_raw",
121 "type": Number}, title: "${_('Commit')}", className: "td-hash" },
124 "type": Number}, title: "${_('Commit')}", className: "td-hash" },
122 { data: {"_": "owner",
125 { data: {"_": "owner",
123 "sort": "owner"}, title: "${_('Owner')}", className: "td-user" }
126 "sort": "owner"}, title: "${_('Owner')}", className: "td-user" }
124 ],
127 ],
125 language: {
128 language: {
126 paginate: DEFAULT_GRID_PAGINATION,
129 paginate: DEFAULT_GRID_PAGINATION,
127 emptyTable: _gettext("No repositories available yet.")
130 emptyTable: _gettext("No repositories available yet.")
128 },
131 },
129 "drawCallback": function( settings, json ) {
132 "drawCallback": function( settings, json ) {
130 timeagoActivate();
133 timeagoActivate();
131 quick_repo_menu();
134 quick_repo_menu();
132 }
135 }
133 });
136 });
134 % endif
137 % endif
135
138
136 });
139 });
137 </script>
140 </script>
138 </%def>
141 </%def>
General Comments 0
You need to be logged in to leave comments. Login now