##// END OF EJS Templates
repository-groups: introduce last change for repository groups.
marcink -
r1940:04fca2a9 default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -0,0 +1,35 b''
1 import logging
2 import datetime
3
4 from sqlalchemy import *
5 from rhodecode.model import meta
6 from rhodecode.lib.dbmigrate.versions import _reset_base, notify
7
8 log = logging.getLogger(__name__)
9
10
11 def upgrade(migrate_engine):
12 """
13 Upgrade operations go here.
14 Don't create your own engine; bind migrate_engine to your metadata
15 """
16 _reset_base(migrate_engine)
17 from rhodecode.lib.dbmigrate.schema import db_4_7_0_1 as db
18
19 repo_group_table = db.RepoGroup.__table__
20
21 updated_on = Column(
22 'updated_on', DateTime(timezone=False), nullable=True, unique=None,
23 default=datetime.datetime.now)
24 updated_on.create(table=repo_group_table)
25
26 fixups(db, meta.Session)
27
28
29 def downgrade(migrate_engine):
30 meta = MetaData()
31 meta.bind = migrate_engine
32
33
34 def fixups(models, _SESSION):
35 pass
@@ -1,63 +1,63 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 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 RhodeCode, a web based repository management software
23 RhodeCode, a web based repository management software
24 versioning implementation: http://www.python.org/dev/peps/pep-0386/
24 versioning implementation: http://www.python.org/dev/peps/pep-0386/
25 """
25 """
26
26
27 import os
27 import os
28 import sys
28 import sys
29 import platform
29 import platform
30
30
31 VERSION = tuple(open(os.path.join(
31 VERSION = tuple(open(os.path.join(
32 os.path.dirname(__file__), 'VERSION')).read().split('.'))
32 os.path.dirname(__file__), 'VERSION')).read().split('.'))
33
33
34 BACKENDS = {
34 BACKENDS = {
35 'hg': 'Mercurial repository',
35 'hg': 'Mercurial repository',
36 'git': 'Git repository',
36 'git': 'Git repository',
37 'svn': 'Subversion repository',
37 'svn': 'Subversion repository',
38 }
38 }
39
39
40 CELERY_ENABLED = False
40 CELERY_ENABLED = False
41 CELERY_EAGER = False
41 CELERY_EAGER = False
42
42
43 # link to config for pylons
43 # link to config for pylons
44 CONFIG = {}
44 CONFIG = {}
45
45
46 # Populated with the settings dictionary from application init in
46 # Populated with the settings dictionary from application init in
47 # rhodecode.conf.environment.load_pyramid_environment
47 # rhodecode.conf.environment.load_pyramid_environment
48 PYRAMID_SETTINGS = {}
48 PYRAMID_SETTINGS = {}
49
49
50 # Linked module for extensions
50 # Linked module for extensions
51 EXTENSIONS = {}
51 EXTENSIONS = {}
52
52
53 __version__ = ('.'.join((str(each) for each in VERSION[:3])))
53 __version__ = ('.'.join((str(each) for each in VERSION[:3])))
54 __dbversion__ = 78 # defines current db version for migrations
54 __dbversion__ = 79 # defines current db version for migrations
55 __platform__ = platform.system()
55 __platform__ = platform.system()
56 __license__ = 'AGPLv3, and Commercial License'
56 __license__ = 'AGPLv3, and Commercial License'
57 __author__ = 'RhodeCode GmbH'
57 __author__ = 'RhodeCode GmbH'
58 __url__ = 'https://code.rhodecode.com'
58 __url__ = 'https://code.rhodecode.com'
59
59
60 is_windows = __platform__ in ['Windows']
60 is_windows = __platform__ in ['Windows']
61 is_unix = not is_windows
61 is_unix = not is_windows
62 is_test = False
62 is_test = False
63 disable_error_handler = False
63 disable_error_handler = False
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,1028 +1,1029 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 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 Repository model for rhodecode
22 Repository model for rhodecode
23 """
23 """
24
24
25 import logging
25 import logging
26 import os
26 import os
27 import re
27 import re
28 import shutil
28 import shutil
29 import time
29 import time
30 import traceback
30 import traceback
31 from datetime import datetime, timedelta
31 import datetime
32
32
33 from pyramid.threadlocal import get_current_request
33 from pyramid.threadlocal import get_current_request
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.lib import helpers as h
37 from rhodecode.lib import helpers as h
38 from rhodecode.lib.auth import HasUserGroupPermissionAny
38 from rhodecode.lib.auth import HasUserGroupPermissionAny
39 from rhodecode.lib.caching_query import FromCache
39 from rhodecode.lib.caching_query import FromCache
40 from rhodecode.lib.exceptions import AttachedForksError
40 from rhodecode.lib.exceptions import AttachedForksError
41 from rhodecode.lib.hooks_base import log_delete_repository
41 from rhodecode.lib.hooks_base import log_delete_repository
42 from rhodecode.lib.utils import make_db_config
42 from rhodecode.lib.utils import make_db_config
43 from rhodecode.lib.utils2 import (
43 from rhodecode.lib.utils2 import (
44 safe_str, safe_unicode, remove_prefix, obfuscate_url_pw,
44 safe_str, safe_unicode, remove_prefix, obfuscate_url_pw,
45 get_current_rhodecode_user, safe_int, datetime_to_time, action_logger_generic)
45 get_current_rhodecode_user, safe_int, datetime_to_time, action_logger_generic)
46 from rhodecode.lib.vcs.backends import get_backend
46 from rhodecode.lib.vcs.backends import get_backend
47 from rhodecode.model import BaseModel
47 from rhodecode.model import BaseModel
48 from rhodecode.model.db import (_hash_key,
48 from rhodecode.model.db import (_hash_key,
49 Repository, UserRepoToPerm, UserGroupRepoToPerm, UserRepoGroupToPerm,
49 Repository, UserRepoToPerm, UserGroupRepoToPerm, UserRepoGroupToPerm,
50 UserGroupRepoGroupToPerm, User, Permission, Statistics, UserGroup,
50 UserGroupRepoGroupToPerm, User, Permission, Statistics, UserGroup,
51 RepoGroup, RepositoryField)
51 RepoGroup, RepositoryField)
52
52
53 from rhodecode.model.settings import VcsSettingsModel
53 from rhodecode.model.settings import VcsSettingsModel
54
54
55
55
56 log = logging.getLogger(__name__)
56 log = logging.getLogger(__name__)
57
57
58
58
59 class RepoModel(BaseModel):
59 class RepoModel(BaseModel):
60
60
61 cls = Repository
61 cls = Repository
62
62
63 def _get_user_group(self, users_group):
63 def _get_user_group(self, users_group):
64 return self._get_instance(UserGroup, users_group,
64 return self._get_instance(UserGroup, users_group,
65 callback=UserGroup.get_by_group_name)
65 callback=UserGroup.get_by_group_name)
66
66
67 def _get_repo_group(self, repo_group):
67 def _get_repo_group(self, repo_group):
68 return self._get_instance(RepoGroup, repo_group,
68 return self._get_instance(RepoGroup, repo_group,
69 callback=RepoGroup.get_by_group_name)
69 callback=RepoGroup.get_by_group_name)
70
70
71 def _create_default_perms(self, repository, private):
71 def _create_default_perms(self, repository, private):
72 # create default permission
72 # create default permission
73 default = 'repository.read'
73 default = 'repository.read'
74 def_user = User.get_default_user()
74 def_user = User.get_default_user()
75 for p in def_user.user_perms:
75 for p in def_user.user_perms:
76 if p.permission.permission_name.startswith('repository.'):
76 if p.permission.permission_name.startswith('repository.'):
77 default = p.permission.permission_name
77 default = p.permission.permission_name
78 break
78 break
79
79
80 default_perm = 'repository.none' if private else default
80 default_perm = 'repository.none' if private else default
81
81
82 repo_to_perm = UserRepoToPerm()
82 repo_to_perm = UserRepoToPerm()
83 repo_to_perm.permission = Permission.get_by_key(default_perm)
83 repo_to_perm.permission = Permission.get_by_key(default_perm)
84
84
85 repo_to_perm.repository = repository
85 repo_to_perm.repository = repository
86 repo_to_perm.user_id = def_user.user_id
86 repo_to_perm.user_id = def_user.user_id
87
87
88 return repo_to_perm
88 return repo_to_perm
89
89
90 @LazyProperty
90 @LazyProperty
91 def repos_path(self):
91 def repos_path(self):
92 """
92 """
93 Gets the repositories root path from database
93 Gets the repositories root path from database
94 """
94 """
95 settings_model = VcsSettingsModel(sa=self.sa)
95 settings_model = VcsSettingsModel(sa=self.sa)
96 return settings_model.get_repos_location()
96 return settings_model.get_repos_location()
97
97
98 def get(self, repo_id, cache=False):
98 def get(self, repo_id, cache=False):
99 repo = self.sa.query(Repository) \
99 repo = self.sa.query(Repository) \
100 .filter(Repository.repo_id == repo_id)
100 .filter(Repository.repo_id == repo_id)
101
101
102 if cache:
102 if cache:
103 repo = repo.options(
103 repo = repo.options(
104 FromCache("sql_cache_short", "get_repo_%s" % repo_id))
104 FromCache("sql_cache_short", "get_repo_%s" % repo_id))
105 return repo.scalar()
105 return repo.scalar()
106
106
107 def get_repo(self, repository):
107 def get_repo(self, repository):
108 return self._get_repo(repository)
108 return self._get_repo(repository)
109
109
110 def get_by_repo_name(self, repo_name, cache=False):
110 def get_by_repo_name(self, repo_name, cache=False):
111 repo = self.sa.query(Repository) \
111 repo = self.sa.query(Repository) \
112 .filter(Repository.repo_name == repo_name)
112 .filter(Repository.repo_name == repo_name)
113
113
114 if cache:
114 if cache:
115 name_key = _hash_key(repo_name)
115 name_key = _hash_key(repo_name)
116 repo = repo.options(
116 repo = repo.options(
117 FromCache("sql_cache_short", "get_repo_%s" % name_key))
117 FromCache("sql_cache_short", "get_repo_%s" % name_key))
118 return repo.scalar()
118 return repo.scalar()
119
119
120 def _extract_id_from_repo_name(self, repo_name):
120 def _extract_id_from_repo_name(self, repo_name):
121 if repo_name.startswith('/'):
121 if repo_name.startswith('/'):
122 repo_name = repo_name.lstrip('/')
122 repo_name = repo_name.lstrip('/')
123 by_id_match = re.match(r'^_(\d{1,})', repo_name)
123 by_id_match = re.match(r'^_(\d{1,})', repo_name)
124 if by_id_match:
124 if by_id_match:
125 return by_id_match.groups()[0]
125 return by_id_match.groups()[0]
126
126
127 def get_repo_by_id(self, repo_name):
127 def get_repo_by_id(self, repo_name):
128 """
128 """
129 Extracts repo_name by id from special urls.
129 Extracts repo_name by id from special urls.
130 Example url is _11/repo_name
130 Example url is _11/repo_name
131
131
132 :param repo_name:
132 :param repo_name:
133 :return: repo object if matched else None
133 :return: repo object if matched else None
134 """
134 """
135
135
136 try:
136 try:
137 _repo_id = self._extract_id_from_repo_name(repo_name)
137 _repo_id = self._extract_id_from_repo_name(repo_name)
138 if _repo_id:
138 if _repo_id:
139 return self.get(_repo_id)
139 return self.get(_repo_id)
140 except Exception:
140 except Exception:
141 log.exception('Failed to extract repo_name from URL')
141 log.exception('Failed to extract repo_name from URL')
142
142
143 return None
143 return None
144
144
145 def get_repos_for_root(self, root, traverse=False):
145 def get_repos_for_root(self, root, traverse=False):
146 if traverse:
146 if traverse:
147 like_expression = u'{}%'.format(safe_unicode(root))
147 like_expression = u'{}%'.format(safe_unicode(root))
148 repos = Repository.query().filter(
148 repos = Repository.query().filter(
149 Repository.repo_name.like(like_expression)).all()
149 Repository.repo_name.like(like_expression)).all()
150 else:
150 else:
151 if root and not isinstance(root, RepoGroup):
151 if root and not isinstance(root, RepoGroup):
152 raise ValueError(
152 raise ValueError(
153 'Root must be an instance '
153 'Root must be an instance '
154 'of RepoGroup, got:{} instead'.format(type(root)))
154 'of RepoGroup, got:{} instead'.format(type(root)))
155 repos = Repository.query().filter(Repository.group == root).all()
155 repos = Repository.query().filter(Repository.group == root).all()
156 return repos
156 return repos
157
157
158 def get_url(self, repo, request=None, permalink=False):
158 def get_url(self, repo, request=None, permalink=False):
159 if not request:
159 if not request:
160 request = get_current_request()
160 request = get_current_request()
161
161
162 if not request:
162 if not request:
163 return
163 return
164
164
165 if permalink:
165 if permalink:
166 return request.route_url(
166 return request.route_url(
167 'repo_summary', repo_name=safe_str(repo.repo_id))
167 'repo_summary', repo_name=safe_str(repo.repo_id))
168 else:
168 else:
169 return request.route_url(
169 return request.route_url(
170 'repo_summary', repo_name=safe_str(repo.repo_name))
170 'repo_summary', repo_name=safe_str(repo.repo_name))
171
171
172 def get_commit_url(self, repo, commit_id, request=None, permalink=False):
172 def get_commit_url(self, repo, commit_id, request=None, permalink=False):
173 if not request:
173 if not request:
174 request = get_current_request()
174 request = get_current_request()
175
175
176 if not request:
176 if not request:
177 return
177 return
178
178
179 if permalink:
179 if permalink:
180 return request.route_url(
180 return request.route_url(
181 'repo_commit', repo_name=safe_str(repo.repo_id),
181 'repo_commit', repo_name=safe_str(repo.repo_id),
182 commit_id=commit_id)
182 commit_id=commit_id)
183
183
184 else:
184 else:
185 return request.route_url(
185 return request.route_url(
186 'repo_commit', repo_name=safe_str(repo.repo_name),
186 'repo_commit', repo_name=safe_str(repo.repo_name),
187 commit_id=commit_id)
187 commit_id=commit_id)
188
188
189 @classmethod
189 @classmethod
190 def update_repoinfo(cls, repositories=None):
190 def update_repoinfo(cls, repositories=None):
191 if not repositories:
191 if not repositories:
192 repositories = Repository.getAll()
192 repositories = Repository.getAll()
193 for repo in repositories:
193 for repo in repositories:
194 repo.update_commit_cache()
194 repo.update_commit_cache()
195
195
196 def get_repos_as_dict(self, repo_list=None, admin=False,
196 def get_repos_as_dict(self, repo_list=None, admin=False,
197 super_user_actions=False):
197 super_user_actions=False):
198 _render = get_current_request().get_partial_renderer(
198 _render = get_current_request().get_partial_renderer(
199 'data_table/_dt_elements.mako')
199 'data_table/_dt_elements.mako')
200 c = _render.get_call_context()
200 c = _render.get_call_context()
201
201
202 def quick_menu(repo_name):
202 def quick_menu(repo_name):
203 return _render('quick_menu', repo_name)
203 return _render('quick_menu', repo_name)
204
204
205 def repo_lnk(name, rtype, rstate, private, fork_of):
205 def repo_lnk(name, rtype, rstate, private, fork_of):
206 return _render('repo_name', name, rtype, rstate, private, fork_of,
206 return _render('repo_name', name, rtype, rstate, private, fork_of,
207 short_name=not admin, admin=False)
207 short_name=not admin, admin=False)
208
208
209 def last_change(last_change):
209 def last_change(last_change):
210 if admin and isinstance(last_change, datetime) and not last_change.tzinfo:
210 if admin and isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
211 last_change = last_change + timedelta(seconds=
211 last_change = last_change + datetime.timedelta(seconds=
212 (datetime.now() - datetime.utcnow()).seconds)
212 (datetime.datetime.now() - datetime.datetime.utcnow()).seconds)
213 return _render("last_change", last_change)
213 return _render("last_change", last_change)
214
214
215 def rss_lnk(repo_name):
215 def rss_lnk(repo_name):
216 return _render("rss", repo_name)
216 return _render("rss", repo_name)
217
217
218 def atom_lnk(repo_name):
218 def atom_lnk(repo_name):
219 return _render("atom", repo_name)
219 return _render("atom", repo_name)
220
220
221 def last_rev(repo_name, cs_cache):
221 def last_rev(repo_name, cs_cache):
222 return _render('revision', repo_name, cs_cache.get('revision'),
222 return _render('revision', repo_name, cs_cache.get('revision'),
223 cs_cache.get('raw_id'), cs_cache.get('author'),
223 cs_cache.get('raw_id'), cs_cache.get('author'),
224 cs_cache.get('message'))
224 cs_cache.get('message'))
225
225
226 def desc(desc):
226 def desc(desc):
227 if c.visual.stylify_metatags:
227 if c.visual.stylify_metatags:
228 desc = h.urlify_text(h.escaped_stylize(desc))
228 desc = h.urlify_text(h.escaped_stylize(desc))
229 else:
229 else:
230 desc = h.urlify_text(h.html_escape(desc))
230 desc = h.urlify_text(h.html_escape(desc))
231
231
232 return _render('repo_desc', desc)
232 return _render('repo_desc', desc)
233
233
234 def state(repo_state):
234 def state(repo_state):
235 return _render("repo_state", repo_state)
235 return _render("repo_state", repo_state)
236
236
237 def repo_actions(repo_name):
237 def repo_actions(repo_name):
238 return _render('repo_actions', repo_name, super_user_actions)
238 return _render('repo_actions', repo_name, super_user_actions)
239
239
240 def user_profile(username):
240 def user_profile(username):
241 return _render('user_profile', username)
241 return _render('user_profile', username)
242
242
243 repos_data = []
243 repos_data = []
244 for repo in repo_list:
244 for repo in repo_list:
245 cs_cache = repo.changeset_cache
245 cs_cache = repo.changeset_cache
246 row = {
246 row = {
247 "menu": quick_menu(repo.repo_name),
247 "menu": quick_menu(repo.repo_name),
248
248
249 "name": repo_lnk(repo.repo_name, repo.repo_type,
249 "name": repo_lnk(repo.repo_name, repo.repo_type,
250 repo.repo_state, repo.private, repo.fork),
250 repo.repo_state, repo.private, repo.fork),
251 "name_raw": repo.repo_name.lower(),
251 "name_raw": repo.repo_name.lower(),
252
252
253 "last_change": last_change(repo.last_db_change),
253 "last_change": last_change(repo.last_db_change),
254 "last_change_raw": datetime_to_time(repo.last_db_change),
254 "last_change_raw": datetime_to_time(repo.last_db_change),
255
255
256 "last_changeset": last_rev(repo.repo_name, cs_cache),
256 "last_changeset": last_rev(repo.repo_name, cs_cache),
257 "last_changeset_raw": cs_cache.get('revision'),
257 "last_changeset_raw": cs_cache.get('revision'),
258
258
259 "desc": desc(repo.description_safe),
259 "desc": desc(repo.description_safe),
260 "owner": user_profile(repo.user.username),
260 "owner": user_profile(repo.user.username),
261
261
262 "state": state(repo.repo_state),
262 "state": state(repo.repo_state),
263 "rss": rss_lnk(repo.repo_name),
263 "rss": rss_lnk(repo.repo_name),
264
264
265 "atom": atom_lnk(repo.repo_name),
265 "atom": atom_lnk(repo.repo_name),
266 }
266 }
267 if admin:
267 if admin:
268 row.update({
268 row.update({
269 "action": repo_actions(repo.repo_name),
269 "action": repo_actions(repo.repo_name),
270 })
270 })
271 repos_data.append(row)
271 repos_data.append(row)
272
272
273 return repos_data
273 return repos_data
274
274
275 def _get_defaults(self, repo_name):
275 def _get_defaults(self, repo_name):
276 """
276 """
277 Gets information about repository, and returns a dict for
277 Gets information about repository, and returns a dict for
278 usage in forms
278 usage in forms
279
279
280 :param repo_name:
280 :param repo_name:
281 """
281 """
282
282
283 repo_info = Repository.get_by_repo_name(repo_name)
283 repo_info = Repository.get_by_repo_name(repo_name)
284
284
285 if repo_info is None:
285 if repo_info is None:
286 return None
286 return None
287
287
288 defaults = repo_info.get_dict()
288 defaults = repo_info.get_dict()
289 defaults['repo_name'] = repo_info.just_name
289 defaults['repo_name'] = repo_info.just_name
290
290
291 groups = repo_info.groups_with_parents
291 groups = repo_info.groups_with_parents
292 parent_group = groups[-1] if groups else None
292 parent_group = groups[-1] if groups else None
293
293
294 # we use -1 as this is how in HTML, we mark an empty group
294 # we use -1 as this is how in HTML, we mark an empty group
295 defaults['repo_group'] = getattr(parent_group, 'group_id', -1)
295 defaults['repo_group'] = getattr(parent_group, 'group_id', -1)
296
296
297 keys_to_process = (
297 keys_to_process = (
298 {'k': 'repo_type', 'strip': False},
298 {'k': 'repo_type', 'strip': False},
299 {'k': 'repo_enable_downloads', 'strip': True},
299 {'k': 'repo_enable_downloads', 'strip': True},
300 {'k': 'repo_description', 'strip': True},
300 {'k': 'repo_description', 'strip': True},
301 {'k': 'repo_enable_locking', 'strip': True},
301 {'k': 'repo_enable_locking', 'strip': True},
302 {'k': 'repo_landing_rev', 'strip': True},
302 {'k': 'repo_landing_rev', 'strip': True},
303 {'k': 'clone_uri', 'strip': False},
303 {'k': 'clone_uri', 'strip': False},
304 {'k': 'repo_private', 'strip': True},
304 {'k': 'repo_private', 'strip': True},
305 {'k': 'repo_enable_statistics', 'strip': True}
305 {'k': 'repo_enable_statistics', 'strip': True}
306 )
306 )
307
307
308 for item in keys_to_process:
308 for item in keys_to_process:
309 attr = item['k']
309 attr = item['k']
310 if item['strip']:
310 if item['strip']:
311 attr = remove_prefix(item['k'], 'repo_')
311 attr = remove_prefix(item['k'], 'repo_')
312
312
313 val = defaults[attr]
313 val = defaults[attr]
314 if item['k'] == 'repo_landing_rev':
314 if item['k'] == 'repo_landing_rev':
315 val = ':'.join(defaults[attr])
315 val = ':'.join(defaults[attr])
316 defaults[item['k']] = val
316 defaults[item['k']] = val
317 if item['k'] == 'clone_uri':
317 if item['k'] == 'clone_uri':
318 defaults['clone_uri_hidden'] = repo_info.clone_uri_hidden
318 defaults['clone_uri_hidden'] = repo_info.clone_uri_hidden
319
319
320 # fill owner
320 # fill owner
321 if repo_info.user:
321 if repo_info.user:
322 defaults.update({'user': repo_info.user.username})
322 defaults.update({'user': repo_info.user.username})
323 else:
323 else:
324 replacement_user = User.get_first_super_admin().username
324 replacement_user = User.get_first_super_admin().username
325 defaults.update({'user': replacement_user})
325 defaults.update({'user': replacement_user})
326
326
327 return defaults
327 return defaults
328
328
329 def update(self, repo, **kwargs):
329 def update(self, repo, **kwargs):
330 try:
330 try:
331 cur_repo = self._get_repo(repo)
331 cur_repo = self._get_repo(repo)
332 source_repo_name = cur_repo.repo_name
332 source_repo_name = cur_repo.repo_name
333 if 'user' in kwargs:
333 if 'user' in kwargs:
334 cur_repo.user = User.get_by_username(kwargs['user'])
334 cur_repo.user = User.get_by_username(kwargs['user'])
335
335
336 if 'repo_group' in kwargs:
336 if 'repo_group' in kwargs:
337 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
337 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
338 log.debug('Updating repo %s with params:%s', cur_repo, kwargs)
338 log.debug('Updating repo %s with params:%s', cur_repo, kwargs)
339
339
340 update_keys = [
340 update_keys = [
341 (1, 'repo_description'),
341 (1, 'repo_description'),
342 (1, 'repo_landing_rev'),
342 (1, 'repo_landing_rev'),
343 (1, 'repo_private'),
343 (1, 'repo_private'),
344 (1, 'repo_enable_downloads'),
344 (1, 'repo_enable_downloads'),
345 (1, 'repo_enable_locking'),
345 (1, 'repo_enable_locking'),
346 (1, 'repo_enable_statistics'),
346 (1, 'repo_enable_statistics'),
347 (0, 'clone_uri'),
347 (0, 'clone_uri'),
348 (0, 'fork_id')
348 (0, 'fork_id')
349 ]
349 ]
350 for strip, k in update_keys:
350 for strip, k in update_keys:
351 if k in kwargs:
351 if k in kwargs:
352 val = kwargs[k]
352 val = kwargs[k]
353 if strip:
353 if strip:
354 k = remove_prefix(k, 'repo_')
354 k = remove_prefix(k, 'repo_')
355
355
356 setattr(cur_repo, k, val)
356 setattr(cur_repo, k, val)
357
357
358 new_name = cur_repo.get_new_name(kwargs['repo_name'])
358 new_name = cur_repo.get_new_name(kwargs['repo_name'])
359 cur_repo.repo_name = new_name
359 cur_repo.repo_name = new_name
360
360
361 # if private flag is set, reset default permission to NONE
361 # if private flag is set, reset default permission to NONE
362 if kwargs.get('repo_private'):
362 if kwargs.get('repo_private'):
363 EMPTY_PERM = 'repository.none'
363 EMPTY_PERM = 'repository.none'
364 RepoModel().grant_user_permission(
364 RepoModel().grant_user_permission(
365 repo=cur_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM
365 repo=cur_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM
366 )
366 )
367
367
368 # handle extra fields
368 # handle extra fields
369 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX),
369 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX),
370 kwargs):
370 kwargs):
371 k = RepositoryField.un_prefix_key(field)
371 k = RepositoryField.un_prefix_key(field)
372 ex_field = RepositoryField.get_by_key_name(
372 ex_field = RepositoryField.get_by_key_name(
373 key=k, repo=cur_repo)
373 key=k, repo=cur_repo)
374 if ex_field:
374 if ex_field:
375 ex_field.field_value = kwargs[field]
375 ex_field.field_value = kwargs[field]
376 self.sa.add(ex_field)
376 self.sa.add(ex_field)
377 cur_repo.updated_on = datetime.datetime.now()
377 self.sa.add(cur_repo)
378 self.sa.add(cur_repo)
378
379
379 if source_repo_name != new_name:
380 if source_repo_name != new_name:
380 # rename repository
381 # rename repository
381 self._rename_filesystem_repo(
382 self._rename_filesystem_repo(
382 old=source_repo_name, new=new_name)
383 old=source_repo_name, new=new_name)
383
384
384 return cur_repo
385 return cur_repo
385 except Exception:
386 except Exception:
386 log.error(traceback.format_exc())
387 log.error(traceback.format_exc())
387 raise
388 raise
388
389
389 def _create_repo(self, repo_name, repo_type, description, owner,
390 def _create_repo(self, repo_name, repo_type, description, owner,
390 private=False, clone_uri=None, repo_group=None,
391 private=False, clone_uri=None, repo_group=None,
391 landing_rev='rev:tip', fork_of=None,
392 landing_rev='rev:tip', fork_of=None,
392 copy_fork_permissions=False, enable_statistics=False,
393 copy_fork_permissions=False, enable_statistics=False,
393 enable_locking=False, enable_downloads=False,
394 enable_locking=False, enable_downloads=False,
394 copy_group_permissions=False,
395 copy_group_permissions=False,
395 state=Repository.STATE_PENDING):
396 state=Repository.STATE_PENDING):
396 """
397 """
397 Create repository inside database with PENDING state, this should be
398 Create repository inside database with PENDING state, this should be
398 only executed by create() repo. With exception of importing existing
399 only executed by create() repo. With exception of importing existing
399 repos
400 repos
400 """
401 """
401 from rhodecode.model.scm import ScmModel
402 from rhodecode.model.scm import ScmModel
402
403
403 owner = self._get_user(owner)
404 owner = self._get_user(owner)
404 fork_of = self._get_repo(fork_of)
405 fork_of = self._get_repo(fork_of)
405 repo_group = self._get_repo_group(safe_int(repo_group))
406 repo_group = self._get_repo_group(safe_int(repo_group))
406
407
407 try:
408 try:
408 repo_name = safe_unicode(repo_name)
409 repo_name = safe_unicode(repo_name)
409 description = safe_unicode(description)
410 description = safe_unicode(description)
410 # repo name is just a name of repository
411 # repo name is just a name of repository
411 # while repo_name_full is a full qualified name that is combined
412 # while repo_name_full is a full qualified name that is combined
412 # with name and path of group
413 # with name and path of group
413 repo_name_full = repo_name
414 repo_name_full = repo_name
414 repo_name = repo_name.split(Repository.NAME_SEP)[-1]
415 repo_name = repo_name.split(Repository.NAME_SEP)[-1]
415
416
416 new_repo = Repository()
417 new_repo = Repository()
417 new_repo.repo_state = state
418 new_repo.repo_state = state
418 new_repo.enable_statistics = False
419 new_repo.enable_statistics = False
419 new_repo.repo_name = repo_name_full
420 new_repo.repo_name = repo_name_full
420 new_repo.repo_type = repo_type
421 new_repo.repo_type = repo_type
421 new_repo.user = owner
422 new_repo.user = owner
422 new_repo.group = repo_group
423 new_repo.group = repo_group
423 new_repo.description = description or repo_name
424 new_repo.description = description or repo_name
424 new_repo.private = private
425 new_repo.private = private
425 new_repo.clone_uri = clone_uri
426 new_repo.clone_uri = clone_uri
426 new_repo.landing_rev = landing_rev
427 new_repo.landing_rev = landing_rev
427
428
428 new_repo.enable_statistics = enable_statistics
429 new_repo.enable_statistics = enable_statistics
429 new_repo.enable_locking = enable_locking
430 new_repo.enable_locking = enable_locking
430 new_repo.enable_downloads = enable_downloads
431 new_repo.enable_downloads = enable_downloads
431
432
432 if repo_group:
433 if repo_group:
433 new_repo.enable_locking = repo_group.enable_locking
434 new_repo.enable_locking = repo_group.enable_locking
434
435
435 if fork_of:
436 if fork_of:
436 parent_repo = fork_of
437 parent_repo = fork_of
437 new_repo.fork = parent_repo
438 new_repo.fork = parent_repo
438
439
439 events.trigger(events.RepoPreCreateEvent(new_repo))
440 events.trigger(events.RepoPreCreateEvent(new_repo))
440
441
441 self.sa.add(new_repo)
442 self.sa.add(new_repo)
442
443
443 EMPTY_PERM = 'repository.none'
444 EMPTY_PERM = 'repository.none'
444 if fork_of and copy_fork_permissions:
445 if fork_of and copy_fork_permissions:
445 repo = fork_of
446 repo = fork_of
446 user_perms = UserRepoToPerm.query() \
447 user_perms = UserRepoToPerm.query() \
447 .filter(UserRepoToPerm.repository == repo).all()
448 .filter(UserRepoToPerm.repository == repo).all()
448 group_perms = UserGroupRepoToPerm.query() \
449 group_perms = UserGroupRepoToPerm.query() \
449 .filter(UserGroupRepoToPerm.repository == repo).all()
450 .filter(UserGroupRepoToPerm.repository == repo).all()
450
451
451 for perm in user_perms:
452 for perm in user_perms:
452 UserRepoToPerm.create(
453 UserRepoToPerm.create(
453 perm.user, new_repo, perm.permission)
454 perm.user, new_repo, perm.permission)
454
455
455 for perm in group_perms:
456 for perm in group_perms:
456 UserGroupRepoToPerm.create(
457 UserGroupRepoToPerm.create(
457 perm.users_group, new_repo, perm.permission)
458 perm.users_group, new_repo, perm.permission)
458 # in case we copy permissions and also set this repo to private
459 # in case we copy permissions and also set this repo to private
459 # override the default user permission to make it a private
460 # override the default user permission to make it a private
460 # repo
461 # repo
461 if private:
462 if private:
462 RepoModel(self.sa).grant_user_permission(
463 RepoModel(self.sa).grant_user_permission(
463 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
464 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
464
465
465 elif repo_group and copy_group_permissions:
466 elif repo_group and copy_group_permissions:
466 user_perms = UserRepoGroupToPerm.query() \
467 user_perms = UserRepoGroupToPerm.query() \
467 .filter(UserRepoGroupToPerm.group == repo_group).all()
468 .filter(UserRepoGroupToPerm.group == repo_group).all()
468
469
469 group_perms = UserGroupRepoGroupToPerm.query() \
470 group_perms = UserGroupRepoGroupToPerm.query() \
470 .filter(UserGroupRepoGroupToPerm.group == repo_group).all()
471 .filter(UserGroupRepoGroupToPerm.group == repo_group).all()
471
472
472 for perm in user_perms:
473 for perm in user_perms:
473 perm_name = perm.permission.permission_name.replace(
474 perm_name = perm.permission.permission_name.replace(
474 'group.', 'repository.')
475 'group.', 'repository.')
475 perm_obj = Permission.get_by_key(perm_name)
476 perm_obj = Permission.get_by_key(perm_name)
476 UserRepoToPerm.create(perm.user, new_repo, perm_obj)
477 UserRepoToPerm.create(perm.user, new_repo, perm_obj)
477
478
478 for perm in group_perms:
479 for perm in group_perms:
479 perm_name = perm.permission.permission_name.replace(
480 perm_name = perm.permission.permission_name.replace(
480 'group.', 'repository.')
481 'group.', 'repository.')
481 perm_obj = Permission.get_by_key(perm_name)
482 perm_obj = Permission.get_by_key(perm_name)
482 UserGroupRepoToPerm.create(
483 UserGroupRepoToPerm.create(
483 perm.users_group, new_repo, perm_obj)
484 perm.users_group, new_repo, perm_obj)
484
485
485 if private:
486 if private:
486 RepoModel(self.sa).grant_user_permission(
487 RepoModel(self.sa).grant_user_permission(
487 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
488 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
488
489
489 else:
490 else:
490 perm_obj = self._create_default_perms(new_repo, private)
491 perm_obj = self._create_default_perms(new_repo, private)
491 self.sa.add(perm_obj)
492 self.sa.add(perm_obj)
492
493
493 # now automatically start following this repository as owner
494 # now automatically start following this repository as owner
494 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
495 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
495 owner.user_id)
496 owner.user_id)
496
497
497 # we need to flush here, in order to check if database won't
498 # we need to flush here, in order to check if database won't
498 # throw any exceptions, create filesystem dirs at the very end
499 # throw any exceptions, create filesystem dirs at the very end
499 self.sa.flush()
500 self.sa.flush()
500 events.trigger(events.RepoCreateEvent(new_repo))
501 events.trigger(events.RepoCreateEvent(new_repo))
501 return new_repo
502 return new_repo
502
503
503 except Exception:
504 except Exception:
504 log.error(traceback.format_exc())
505 log.error(traceback.format_exc())
505 raise
506 raise
506
507
507 def create(self, form_data, cur_user):
508 def create(self, form_data, cur_user):
508 """
509 """
509 Create repository using celery tasks
510 Create repository using celery tasks
510
511
511 :param form_data:
512 :param form_data:
512 :param cur_user:
513 :param cur_user:
513 """
514 """
514 from rhodecode.lib.celerylib import tasks, run_task
515 from rhodecode.lib.celerylib import tasks, run_task
515 return run_task(tasks.create_repo, form_data, cur_user)
516 return run_task(tasks.create_repo, form_data, cur_user)
516
517
517 def update_permissions(self, repo, perm_additions=None, perm_updates=None,
518 def update_permissions(self, repo, perm_additions=None, perm_updates=None,
518 perm_deletions=None, check_perms=True,
519 perm_deletions=None, check_perms=True,
519 cur_user=None):
520 cur_user=None):
520 if not perm_additions:
521 if not perm_additions:
521 perm_additions = []
522 perm_additions = []
522 if not perm_updates:
523 if not perm_updates:
523 perm_updates = []
524 perm_updates = []
524 if not perm_deletions:
525 if not perm_deletions:
525 perm_deletions = []
526 perm_deletions = []
526
527
527 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
528 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
528
529
529 changes = {
530 changes = {
530 'added': [],
531 'added': [],
531 'updated': [],
532 'updated': [],
532 'deleted': []
533 'deleted': []
533 }
534 }
534 # update permissions
535 # update permissions
535 for member_id, perm, member_type in perm_updates:
536 for member_id, perm, member_type in perm_updates:
536 member_id = int(member_id)
537 member_id = int(member_id)
537 if member_type == 'user':
538 if member_type == 'user':
538 member_name = User.get(member_id).username
539 member_name = User.get(member_id).username
539 # this updates also current one if found
540 # this updates also current one if found
540 self.grant_user_permission(
541 self.grant_user_permission(
541 repo=repo, user=member_id, perm=perm)
542 repo=repo, user=member_id, perm=perm)
542 else: # set for user group
543 else: # set for user group
543 # check if we have permissions to alter this usergroup
544 # check if we have permissions to alter this usergroup
544 member_name = UserGroup.get(member_id).users_group_name
545 member_name = UserGroup.get(member_id).users_group_name
545 if not check_perms or HasUserGroupPermissionAny(
546 if not check_perms or HasUserGroupPermissionAny(
546 *req_perms)(member_name, user=cur_user):
547 *req_perms)(member_name, user=cur_user):
547 self.grant_user_group_permission(
548 self.grant_user_group_permission(
548 repo=repo, group_name=member_id, perm=perm)
549 repo=repo, group_name=member_id, perm=perm)
549
550
550 changes['updated'].append({'type': member_type, 'id': member_id,
551 changes['updated'].append({'type': member_type, 'id': member_id,
551 'name': member_name, 'new_perm': perm})
552 'name': member_name, 'new_perm': perm})
552
553
553 # set new permissions
554 # set new permissions
554 for member_id, perm, member_type in perm_additions:
555 for member_id, perm, member_type in perm_additions:
555 member_id = int(member_id)
556 member_id = int(member_id)
556 if member_type == 'user':
557 if member_type == 'user':
557 member_name = User.get(member_id).username
558 member_name = User.get(member_id).username
558 self.grant_user_permission(
559 self.grant_user_permission(
559 repo=repo, user=member_id, perm=perm)
560 repo=repo, user=member_id, perm=perm)
560 else: # set for user group
561 else: # set for user group
561 # check if we have permissions to alter this usergroup
562 # check if we have permissions to alter this usergroup
562 member_name = UserGroup.get(member_id).users_group_name
563 member_name = UserGroup.get(member_id).users_group_name
563 if not check_perms or HasUserGroupPermissionAny(
564 if not check_perms or HasUserGroupPermissionAny(
564 *req_perms)(member_name, user=cur_user):
565 *req_perms)(member_name, user=cur_user):
565 self.grant_user_group_permission(
566 self.grant_user_group_permission(
566 repo=repo, group_name=member_id, perm=perm)
567 repo=repo, group_name=member_id, perm=perm)
567 changes['added'].append({'type': member_type, 'id': member_id,
568 changes['added'].append({'type': member_type, 'id': member_id,
568 'name': member_name, 'new_perm': perm})
569 'name': member_name, 'new_perm': perm})
569 # delete permissions
570 # delete permissions
570 for member_id, perm, member_type in perm_deletions:
571 for member_id, perm, member_type in perm_deletions:
571 member_id = int(member_id)
572 member_id = int(member_id)
572 if member_type == 'user':
573 if member_type == 'user':
573 member_name = User.get(member_id).username
574 member_name = User.get(member_id).username
574 self.revoke_user_permission(repo=repo, user=member_id)
575 self.revoke_user_permission(repo=repo, user=member_id)
575 else: # set for user group
576 else: # set for user group
576 # check if we have permissions to alter this usergroup
577 # check if we have permissions to alter this usergroup
577 member_name = UserGroup.get(member_id).users_group_name
578 member_name = UserGroup.get(member_id).users_group_name
578 if not check_perms or HasUserGroupPermissionAny(
579 if not check_perms or HasUserGroupPermissionAny(
579 *req_perms)(member_name, user=cur_user):
580 *req_perms)(member_name, user=cur_user):
580 self.revoke_user_group_permission(
581 self.revoke_user_group_permission(
581 repo=repo, group_name=member_id)
582 repo=repo, group_name=member_id)
582
583
583 changes['deleted'].append({'type': member_type, 'id': member_id,
584 changes['deleted'].append({'type': member_type, 'id': member_id,
584 'name': member_name, 'new_perm': perm})
585 'name': member_name, 'new_perm': perm})
585 return changes
586 return changes
586
587
587 def create_fork(self, form_data, cur_user):
588 def create_fork(self, form_data, cur_user):
588 """
589 """
589 Simple wrapper into executing celery task for fork creation
590 Simple wrapper into executing celery task for fork creation
590
591
591 :param form_data:
592 :param form_data:
592 :param cur_user:
593 :param cur_user:
593 """
594 """
594 from rhodecode.lib.celerylib import tasks, run_task
595 from rhodecode.lib.celerylib import tasks, run_task
595 return run_task(tasks.create_repo_fork, form_data, cur_user)
596 return run_task(tasks.create_repo_fork, form_data, cur_user)
596
597
597 def delete(self, repo, forks=None, fs_remove=True, cur_user=None):
598 def delete(self, repo, forks=None, fs_remove=True, cur_user=None):
598 """
599 """
599 Delete given repository, forks parameter defines what do do with
600 Delete given repository, forks parameter defines what do do with
600 attached forks. Throws AttachedForksError if deleted repo has attached
601 attached forks. Throws AttachedForksError if deleted repo has attached
601 forks
602 forks
602
603
603 :param repo:
604 :param repo:
604 :param forks: str 'delete' or 'detach'
605 :param forks: str 'delete' or 'detach'
605 :param fs_remove: remove(archive) repo from filesystem
606 :param fs_remove: remove(archive) repo from filesystem
606 """
607 """
607 if not cur_user:
608 if not cur_user:
608 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
609 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
609 repo = self._get_repo(repo)
610 repo = self._get_repo(repo)
610 if repo:
611 if repo:
611 if forks == 'detach':
612 if forks == 'detach':
612 for r in repo.forks:
613 for r in repo.forks:
613 r.fork = None
614 r.fork = None
614 self.sa.add(r)
615 self.sa.add(r)
615 elif forks == 'delete':
616 elif forks == 'delete':
616 for r in repo.forks:
617 for r in repo.forks:
617 self.delete(r, forks='delete')
618 self.delete(r, forks='delete')
618 elif [f for f in repo.forks]:
619 elif [f for f in repo.forks]:
619 raise AttachedForksError()
620 raise AttachedForksError()
620
621
621 old_repo_dict = repo.get_dict()
622 old_repo_dict = repo.get_dict()
622 events.trigger(events.RepoPreDeleteEvent(repo))
623 events.trigger(events.RepoPreDeleteEvent(repo))
623 try:
624 try:
624 self.sa.delete(repo)
625 self.sa.delete(repo)
625 if fs_remove:
626 if fs_remove:
626 self._delete_filesystem_repo(repo)
627 self._delete_filesystem_repo(repo)
627 else:
628 else:
628 log.debug('skipping removal from filesystem')
629 log.debug('skipping removal from filesystem')
629 old_repo_dict.update({
630 old_repo_dict.update({
630 'deleted_by': cur_user,
631 'deleted_by': cur_user,
631 'deleted_on': time.time(),
632 'deleted_on': time.time(),
632 })
633 })
633 log_delete_repository(**old_repo_dict)
634 log_delete_repository(**old_repo_dict)
634 events.trigger(events.RepoDeleteEvent(repo))
635 events.trigger(events.RepoDeleteEvent(repo))
635 except Exception:
636 except Exception:
636 log.error(traceback.format_exc())
637 log.error(traceback.format_exc())
637 raise
638 raise
638
639
639 def grant_user_permission(self, repo, user, perm):
640 def grant_user_permission(self, repo, user, perm):
640 """
641 """
641 Grant permission for user on given repository, or update existing one
642 Grant permission for user on given repository, or update existing one
642 if found
643 if found
643
644
644 :param repo: Instance of Repository, repository_id, or repository name
645 :param repo: Instance of Repository, repository_id, or repository name
645 :param user: Instance of User, user_id or username
646 :param user: Instance of User, user_id or username
646 :param perm: Instance of Permission, or permission_name
647 :param perm: Instance of Permission, or permission_name
647 """
648 """
648 user = self._get_user(user)
649 user = self._get_user(user)
649 repo = self._get_repo(repo)
650 repo = self._get_repo(repo)
650 permission = self._get_perm(perm)
651 permission = self._get_perm(perm)
651
652
652 # check if we have that permission already
653 # check if we have that permission already
653 obj = self.sa.query(UserRepoToPerm) \
654 obj = self.sa.query(UserRepoToPerm) \
654 .filter(UserRepoToPerm.user == user) \
655 .filter(UserRepoToPerm.user == user) \
655 .filter(UserRepoToPerm.repository == repo) \
656 .filter(UserRepoToPerm.repository == repo) \
656 .scalar()
657 .scalar()
657 if obj is None:
658 if obj is None:
658 # create new !
659 # create new !
659 obj = UserRepoToPerm()
660 obj = UserRepoToPerm()
660 obj.repository = repo
661 obj.repository = repo
661 obj.user = user
662 obj.user = user
662 obj.permission = permission
663 obj.permission = permission
663 self.sa.add(obj)
664 self.sa.add(obj)
664 log.debug('Granted perm %s to %s on %s', perm, user, repo)
665 log.debug('Granted perm %s to %s on %s', perm, user, repo)
665 action_logger_generic(
666 action_logger_generic(
666 'granted permission: {} to user: {} on repo: {}'.format(
667 'granted permission: {} to user: {} on repo: {}'.format(
667 perm, user, repo), namespace='security.repo')
668 perm, user, repo), namespace='security.repo')
668 return obj
669 return obj
669
670
670 def revoke_user_permission(self, repo, user):
671 def revoke_user_permission(self, repo, user):
671 """
672 """
672 Revoke permission for user on given repository
673 Revoke permission for user on given repository
673
674
674 :param repo: Instance of Repository, repository_id, or repository name
675 :param repo: Instance of Repository, repository_id, or repository name
675 :param user: Instance of User, user_id or username
676 :param user: Instance of User, user_id or username
676 """
677 """
677
678
678 user = self._get_user(user)
679 user = self._get_user(user)
679 repo = self._get_repo(repo)
680 repo = self._get_repo(repo)
680
681
681 obj = self.sa.query(UserRepoToPerm) \
682 obj = self.sa.query(UserRepoToPerm) \
682 .filter(UserRepoToPerm.repository == repo) \
683 .filter(UserRepoToPerm.repository == repo) \
683 .filter(UserRepoToPerm.user == user) \
684 .filter(UserRepoToPerm.user == user) \
684 .scalar()
685 .scalar()
685 if obj:
686 if obj:
686 self.sa.delete(obj)
687 self.sa.delete(obj)
687 log.debug('Revoked perm on %s on %s', repo, user)
688 log.debug('Revoked perm on %s on %s', repo, user)
688 action_logger_generic(
689 action_logger_generic(
689 'revoked permission from user: {} on repo: {}'.format(
690 'revoked permission from user: {} on repo: {}'.format(
690 user, repo), namespace='security.repo')
691 user, repo), namespace='security.repo')
691
692
692 def grant_user_group_permission(self, repo, group_name, perm):
693 def grant_user_group_permission(self, repo, group_name, perm):
693 """
694 """
694 Grant permission for user group on given repository, or update
695 Grant permission for user group on given repository, or update
695 existing one if found
696 existing one if found
696
697
697 :param repo: Instance of Repository, repository_id, or repository name
698 :param repo: Instance of Repository, repository_id, or repository name
698 :param group_name: Instance of UserGroup, users_group_id,
699 :param group_name: Instance of UserGroup, users_group_id,
699 or user group name
700 or user group name
700 :param perm: Instance of Permission, or permission_name
701 :param perm: Instance of Permission, or permission_name
701 """
702 """
702 repo = self._get_repo(repo)
703 repo = self._get_repo(repo)
703 group_name = self._get_user_group(group_name)
704 group_name = self._get_user_group(group_name)
704 permission = self._get_perm(perm)
705 permission = self._get_perm(perm)
705
706
706 # check if we have that permission already
707 # check if we have that permission already
707 obj = self.sa.query(UserGroupRepoToPerm) \
708 obj = self.sa.query(UserGroupRepoToPerm) \
708 .filter(UserGroupRepoToPerm.users_group == group_name) \
709 .filter(UserGroupRepoToPerm.users_group == group_name) \
709 .filter(UserGroupRepoToPerm.repository == repo) \
710 .filter(UserGroupRepoToPerm.repository == repo) \
710 .scalar()
711 .scalar()
711
712
712 if obj is None:
713 if obj is None:
713 # create new
714 # create new
714 obj = UserGroupRepoToPerm()
715 obj = UserGroupRepoToPerm()
715
716
716 obj.repository = repo
717 obj.repository = repo
717 obj.users_group = group_name
718 obj.users_group = group_name
718 obj.permission = permission
719 obj.permission = permission
719 self.sa.add(obj)
720 self.sa.add(obj)
720 log.debug('Granted perm %s to %s on %s', perm, group_name, repo)
721 log.debug('Granted perm %s to %s on %s', perm, group_name, repo)
721 action_logger_generic(
722 action_logger_generic(
722 'granted permission: {} to usergroup: {} on repo: {}'.format(
723 'granted permission: {} to usergroup: {} on repo: {}'.format(
723 perm, group_name, repo), namespace='security.repo')
724 perm, group_name, repo), namespace='security.repo')
724
725
725 return obj
726 return obj
726
727
727 def revoke_user_group_permission(self, repo, group_name):
728 def revoke_user_group_permission(self, repo, group_name):
728 """
729 """
729 Revoke permission for user group on given repository
730 Revoke permission for user group on given repository
730
731
731 :param repo: Instance of Repository, repository_id, or repository name
732 :param repo: Instance of Repository, repository_id, or repository name
732 :param group_name: Instance of UserGroup, users_group_id,
733 :param group_name: Instance of UserGroup, users_group_id,
733 or user group name
734 or user group name
734 """
735 """
735 repo = self._get_repo(repo)
736 repo = self._get_repo(repo)
736 group_name = self._get_user_group(group_name)
737 group_name = self._get_user_group(group_name)
737
738
738 obj = self.sa.query(UserGroupRepoToPerm) \
739 obj = self.sa.query(UserGroupRepoToPerm) \
739 .filter(UserGroupRepoToPerm.repository == repo) \
740 .filter(UserGroupRepoToPerm.repository == repo) \
740 .filter(UserGroupRepoToPerm.users_group == group_name) \
741 .filter(UserGroupRepoToPerm.users_group == group_name) \
741 .scalar()
742 .scalar()
742 if obj:
743 if obj:
743 self.sa.delete(obj)
744 self.sa.delete(obj)
744 log.debug('Revoked perm to %s on %s', repo, group_name)
745 log.debug('Revoked perm to %s on %s', repo, group_name)
745 action_logger_generic(
746 action_logger_generic(
746 'revoked permission from usergroup: {} on repo: {}'.format(
747 'revoked permission from usergroup: {} on repo: {}'.format(
747 group_name, repo), namespace='security.repo')
748 group_name, repo), namespace='security.repo')
748
749
749 def delete_stats(self, repo_name):
750 def delete_stats(self, repo_name):
750 """
751 """
751 removes stats for given repo
752 removes stats for given repo
752
753
753 :param repo_name:
754 :param repo_name:
754 """
755 """
755 repo = self._get_repo(repo_name)
756 repo = self._get_repo(repo_name)
756 try:
757 try:
757 obj = self.sa.query(Statistics) \
758 obj = self.sa.query(Statistics) \
758 .filter(Statistics.repository == repo).scalar()
759 .filter(Statistics.repository == repo).scalar()
759 if obj:
760 if obj:
760 self.sa.delete(obj)
761 self.sa.delete(obj)
761 except Exception:
762 except Exception:
762 log.error(traceback.format_exc())
763 log.error(traceback.format_exc())
763 raise
764 raise
764
765
765 def add_repo_field(self, repo_name, field_key, field_label, field_value='',
766 def add_repo_field(self, repo_name, field_key, field_label, field_value='',
766 field_type='str', field_desc=''):
767 field_type='str', field_desc=''):
767
768
768 repo = self._get_repo(repo_name)
769 repo = self._get_repo(repo_name)
769
770
770 new_field = RepositoryField()
771 new_field = RepositoryField()
771 new_field.repository = repo
772 new_field.repository = repo
772 new_field.field_key = field_key
773 new_field.field_key = field_key
773 new_field.field_type = field_type # python type
774 new_field.field_type = field_type # python type
774 new_field.field_value = field_value
775 new_field.field_value = field_value
775 new_field.field_desc = field_desc
776 new_field.field_desc = field_desc
776 new_field.field_label = field_label
777 new_field.field_label = field_label
777 self.sa.add(new_field)
778 self.sa.add(new_field)
778 return new_field
779 return new_field
779
780
780 def delete_repo_field(self, repo_name, field_key):
781 def delete_repo_field(self, repo_name, field_key):
781 repo = self._get_repo(repo_name)
782 repo = self._get_repo(repo_name)
782 field = RepositoryField.get_by_key_name(field_key, repo)
783 field = RepositoryField.get_by_key_name(field_key, repo)
783 if field:
784 if field:
784 self.sa.delete(field)
785 self.sa.delete(field)
785
786
786 def _create_filesystem_repo(self, repo_name, repo_type, repo_group,
787 def _create_filesystem_repo(self, repo_name, repo_type, repo_group,
787 clone_uri=None, repo_store_location=None,
788 clone_uri=None, repo_store_location=None,
788 use_global_config=False):
789 use_global_config=False):
789 """
790 """
790 makes repository on filesystem. It's group aware means it'll create
791 makes repository on filesystem. It's group aware means it'll create
791 a repository within a group, and alter the paths accordingly of
792 a repository within a group, and alter the paths accordingly of
792 group location
793 group location
793
794
794 :param repo_name:
795 :param repo_name:
795 :param alias:
796 :param alias:
796 :param parent:
797 :param parent:
797 :param clone_uri:
798 :param clone_uri:
798 :param repo_store_location:
799 :param repo_store_location:
799 """
800 """
800 from rhodecode.lib.utils import is_valid_repo, is_valid_repo_group
801 from rhodecode.lib.utils import is_valid_repo, is_valid_repo_group
801 from rhodecode.model.scm import ScmModel
802 from rhodecode.model.scm import ScmModel
802
803
803 if Repository.NAME_SEP in repo_name:
804 if Repository.NAME_SEP in repo_name:
804 raise ValueError(
805 raise ValueError(
805 'repo_name must not contain groups got `%s`' % repo_name)
806 'repo_name must not contain groups got `%s`' % repo_name)
806
807
807 if isinstance(repo_group, RepoGroup):
808 if isinstance(repo_group, RepoGroup):
808 new_parent_path = os.sep.join(repo_group.full_path_splitted)
809 new_parent_path = os.sep.join(repo_group.full_path_splitted)
809 else:
810 else:
810 new_parent_path = repo_group or ''
811 new_parent_path = repo_group or ''
811
812
812 if repo_store_location:
813 if repo_store_location:
813 _paths = [repo_store_location]
814 _paths = [repo_store_location]
814 else:
815 else:
815 _paths = [self.repos_path, new_parent_path, repo_name]
816 _paths = [self.repos_path, new_parent_path, repo_name]
816 # we need to make it str for mercurial
817 # we need to make it str for mercurial
817 repo_path = os.path.join(*map(lambda x: safe_str(x), _paths))
818 repo_path = os.path.join(*map(lambda x: safe_str(x), _paths))
818
819
819 # check if this path is not a repository
820 # check if this path is not a repository
820 if is_valid_repo(repo_path, self.repos_path):
821 if is_valid_repo(repo_path, self.repos_path):
821 raise Exception('This path %s is a valid repository' % repo_path)
822 raise Exception('This path %s is a valid repository' % repo_path)
822
823
823 # check if this path is a group
824 # check if this path is a group
824 if is_valid_repo_group(repo_path, self.repos_path):
825 if is_valid_repo_group(repo_path, self.repos_path):
825 raise Exception('This path %s is a valid group' % repo_path)
826 raise Exception('This path %s is a valid group' % repo_path)
826
827
827 log.info('creating repo %s in %s from url: `%s`',
828 log.info('creating repo %s in %s from url: `%s`',
828 repo_name, safe_unicode(repo_path),
829 repo_name, safe_unicode(repo_path),
829 obfuscate_url_pw(clone_uri))
830 obfuscate_url_pw(clone_uri))
830
831
831 backend = get_backend(repo_type)
832 backend = get_backend(repo_type)
832
833
833 config_repo = None if use_global_config else repo_name
834 config_repo = None if use_global_config else repo_name
834 if config_repo and new_parent_path:
835 if config_repo and new_parent_path:
835 config_repo = Repository.NAME_SEP.join(
836 config_repo = Repository.NAME_SEP.join(
836 (new_parent_path, config_repo))
837 (new_parent_path, config_repo))
837 config = make_db_config(clear_session=False, repo=config_repo)
838 config = make_db_config(clear_session=False, repo=config_repo)
838 config.set('extensions', 'largefiles', '')
839 config.set('extensions', 'largefiles', '')
839
840
840 # patch and reset hooks section of UI config to not run any
841 # patch and reset hooks section of UI config to not run any
841 # hooks on creating remote repo
842 # hooks on creating remote repo
842 config.clear_section('hooks')
843 config.clear_section('hooks')
843
844
844 # TODO: johbo: Unify this, hardcoded "bare=True" does not look nice
845 # TODO: johbo: Unify this, hardcoded "bare=True" does not look nice
845 if repo_type == 'git':
846 if repo_type == 'git':
846 repo = backend(
847 repo = backend(
847 repo_path, config=config, create=True, src_url=clone_uri,
848 repo_path, config=config, create=True, src_url=clone_uri,
848 bare=True)
849 bare=True)
849 else:
850 else:
850 repo = backend(
851 repo = backend(
851 repo_path, config=config, create=True, src_url=clone_uri)
852 repo_path, config=config, create=True, src_url=clone_uri)
852
853
853 ScmModel().install_hooks(repo, repo_type=repo_type)
854 ScmModel().install_hooks(repo, repo_type=repo_type)
854
855
855 log.debug('Created repo %s with %s backend',
856 log.debug('Created repo %s with %s backend',
856 safe_unicode(repo_name), safe_unicode(repo_type))
857 safe_unicode(repo_name), safe_unicode(repo_type))
857 return repo
858 return repo
858
859
859 def _rename_filesystem_repo(self, old, new):
860 def _rename_filesystem_repo(self, old, new):
860 """
861 """
861 renames repository on filesystem
862 renames repository on filesystem
862
863
863 :param old: old name
864 :param old: old name
864 :param new: new name
865 :param new: new name
865 """
866 """
866 log.info('renaming repo from %s to %s', old, new)
867 log.info('renaming repo from %s to %s', old, new)
867
868
868 old_path = os.path.join(self.repos_path, old)
869 old_path = os.path.join(self.repos_path, old)
869 new_path = os.path.join(self.repos_path, new)
870 new_path = os.path.join(self.repos_path, new)
870 if os.path.isdir(new_path):
871 if os.path.isdir(new_path):
871 raise Exception(
872 raise Exception(
872 'Was trying to rename to already existing dir %s' % new_path
873 'Was trying to rename to already existing dir %s' % new_path
873 )
874 )
874 shutil.move(old_path, new_path)
875 shutil.move(old_path, new_path)
875
876
876 def _delete_filesystem_repo(self, repo):
877 def _delete_filesystem_repo(self, repo):
877 """
878 """
878 removes repo from filesystem, the removal is acctually made by
879 removes repo from filesystem, the removal is acctually made by
879 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
880 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
880 repository is no longer valid for rhodecode, can be undeleted later on
881 repository is no longer valid for rhodecode, can be undeleted later on
881 by reverting the renames on this repository
882 by reverting the renames on this repository
882
883
883 :param repo: repo object
884 :param repo: repo object
884 """
885 """
885 rm_path = os.path.join(self.repos_path, repo.repo_name)
886 rm_path = os.path.join(self.repos_path, repo.repo_name)
886 repo_group = repo.group
887 repo_group = repo.group
887 log.info("Removing repository %s", rm_path)
888 log.info("Removing repository %s", rm_path)
888 # disable hg/git internal that it doesn't get detected as repo
889 # disable hg/git internal that it doesn't get detected as repo
889 alias = repo.repo_type
890 alias = repo.repo_type
890
891
891 config = make_db_config(clear_session=False)
892 config = make_db_config(clear_session=False)
892 config.set('extensions', 'largefiles', '')
893 config.set('extensions', 'largefiles', '')
893 bare = getattr(repo.scm_instance(config=config), 'bare', False)
894 bare = getattr(repo.scm_instance(config=config), 'bare', False)
894
895
895 # skip this for bare git repos
896 # skip this for bare git repos
896 if not bare:
897 if not bare:
897 # disable VCS repo
898 # disable VCS repo
898 vcs_path = os.path.join(rm_path, '.%s' % alias)
899 vcs_path = os.path.join(rm_path, '.%s' % alias)
899 if os.path.exists(vcs_path):
900 if os.path.exists(vcs_path):
900 shutil.move(vcs_path, os.path.join(rm_path, 'rm__.%s' % alias))
901 shutil.move(vcs_path, os.path.join(rm_path, 'rm__.%s' % alias))
901
902
902 _now = datetime.now()
903 _now = datetime.datetime.now()
903 _ms = str(_now.microsecond).rjust(6, '0')
904 _ms = str(_now.microsecond).rjust(6, '0')
904 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
905 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
905 repo.just_name)
906 repo.just_name)
906 if repo_group:
907 if repo_group:
907 # if repository is in group, prefix the removal path with the group
908 # if repository is in group, prefix the removal path with the group
908 args = repo_group.full_path_splitted + [_d]
909 args = repo_group.full_path_splitted + [_d]
909 _d = os.path.join(*args)
910 _d = os.path.join(*args)
910
911
911 if os.path.isdir(rm_path):
912 if os.path.isdir(rm_path):
912 shutil.move(rm_path, os.path.join(self.repos_path, _d))
913 shutil.move(rm_path, os.path.join(self.repos_path, _d))
913
914
914
915
915 class ReadmeFinder:
916 class ReadmeFinder:
916 """
917 """
917 Utility which knows how to find a readme for a specific commit.
918 Utility which knows how to find a readme for a specific commit.
918
919
919 The main idea is that this is a configurable algorithm. When creating an
920 The main idea is that this is a configurable algorithm. When creating an
920 instance you can define parameters, currently only the `default_renderer`.
921 instance you can define parameters, currently only the `default_renderer`.
921 Based on this configuration the method :meth:`search` behaves slightly
922 Based on this configuration the method :meth:`search` behaves slightly
922 different.
923 different.
923 """
924 """
924
925
925 readme_re = re.compile(r'^readme(\.[^\.]+)?$', re.IGNORECASE)
926 readme_re = re.compile(r'^readme(\.[^\.]+)?$', re.IGNORECASE)
926 path_re = re.compile(r'^docs?', re.IGNORECASE)
927 path_re = re.compile(r'^docs?', re.IGNORECASE)
927
928
928 default_priorities = {
929 default_priorities = {
929 None: 0,
930 None: 0,
930 '.text': 2,
931 '.text': 2,
931 '.txt': 3,
932 '.txt': 3,
932 '.rst': 1,
933 '.rst': 1,
933 '.rest': 2,
934 '.rest': 2,
934 '.md': 1,
935 '.md': 1,
935 '.mkdn': 2,
936 '.mkdn': 2,
936 '.mdown': 3,
937 '.mdown': 3,
937 '.markdown': 4,
938 '.markdown': 4,
938 }
939 }
939
940
940 path_priority = {
941 path_priority = {
941 'doc': 0,
942 'doc': 0,
942 'docs': 1,
943 'docs': 1,
943 }
944 }
944
945
945 FALLBACK_PRIORITY = 99
946 FALLBACK_PRIORITY = 99
946
947
947 RENDERER_TO_EXTENSION = {
948 RENDERER_TO_EXTENSION = {
948 'rst': ['.rst', '.rest'],
949 'rst': ['.rst', '.rest'],
949 'markdown': ['.md', 'mkdn', '.mdown', '.markdown'],
950 'markdown': ['.md', 'mkdn', '.mdown', '.markdown'],
950 }
951 }
951
952
952 def __init__(self, default_renderer=None):
953 def __init__(self, default_renderer=None):
953 self._default_renderer = default_renderer
954 self._default_renderer = default_renderer
954 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(
955 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(
955 default_renderer, [])
956 default_renderer, [])
956
957
957 def search(self, commit, path='/'):
958 def search(self, commit, path='/'):
958 """
959 """
959 Find a readme in the given `commit`.
960 Find a readme in the given `commit`.
960 """
961 """
961 nodes = commit.get_nodes(path)
962 nodes = commit.get_nodes(path)
962 matches = self._match_readmes(nodes)
963 matches = self._match_readmes(nodes)
963 matches = self._sort_according_to_priority(matches)
964 matches = self._sort_according_to_priority(matches)
964 if matches:
965 if matches:
965 return matches[0].node
966 return matches[0].node
966
967
967 paths = self._match_paths(nodes)
968 paths = self._match_paths(nodes)
968 paths = self._sort_paths_according_to_priority(paths)
969 paths = self._sort_paths_according_to_priority(paths)
969 for path in paths:
970 for path in paths:
970 match = self.search(commit, path=path)
971 match = self.search(commit, path=path)
971 if match:
972 if match:
972 return match
973 return match
973
974
974 return None
975 return None
975
976
976 def _match_readmes(self, nodes):
977 def _match_readmes(self, nodes):
977 for node in nodes:
978 for node in nodes:
978 if not node.is_file():
979 if not node.is_file():
979 continue
980 continue
980 path = node.path.rsplit('/', 1)[-1]
981 path = node.path.rsplit('/', 1)[-1]
981 match = self.readme_re.match(path)
982 match = self.readme_re.match(path)
982 if match:
983 if match:
983 extension = match.group(1)
984 extension = match.group(1)
984 yield ReadmeMatch(node, match, self._priority(extension))
985 yield ReadmeMatch(node, match, self._priority(extension))
985
986
986 def _match_paths(self, nodes):
987 def _match_paths(self, nodes):
987 for node in nodes:
988 for node in nodes:
988 if not node.is_dir():
989 if not node.is_dir():
989 continue
990 continue
990 match = self.path_re.match(node.path)
991 match = self.path_re.match(node.path)
991 if match:
992 if match:
992 yield node.path
993 yield node.path
993
994
994 def _priority(self, extension):
995 def _priority(self, extension):
995 renderer_priority = (
996 renderer_priority = (
996 0 if extension in self._renderer_extensions else 1)
997 0 if extension in self._renderer_extensions else 1)
997 extension_priority = self.default_priorities.get(
998 extension_priority = self.default_priorities.get(
998 extension, self.FALLBACK_PRIORITY)
999 extension, self.FALLBACK_PRIORITY)
999 return (renderer_priority, extension_priority)
1000 return (renderer_priority, extension_priority)
1000
1001
1001 def _sort_according_to_priority(self, matches):
1002 def _sort_according_to_priority(self, matches):
1002
1003
1003 def priority_and_path(match):
1004 def priority_and_path(match):
1004 return (match.priority, match.path)
1005 return (match.priority, match.path)
1005
1006
1006 return sorted(matches, key=priority_and_path)
1007 return sorted(matches, key=priority_and_path)
1007
1008
1008 def _sort_paths_according_to_priority(self, paths):
1009 def _sort_paths_according_to_priority(self, paths):
1009
1010
1010 def priority_and_path(path):
1011 def priority_and_path(path):
1011 return (self.path_priority.get(path, self.FALLBACK_PRIORITY), path)
1012 return (self.path_priority.get(path, self.FALLBACK_PRIORITY), path)
1012
1013
1013 return sorted(paths, key=priority_and_path)
1014 return sorted(paths, key=priority_and_path)
1014
1015
1015
1016
1016 class ReadmeMatch:
1017 class ReadmeMatch:
1017
1018
1018 def __init__(self, node, match, priority):
1019 def __init__(self, node, match, priority):
1019 self.node = node
1020 self.node = node
1020 self._match = match
1021 self._match = match
1021 self.priority = priority
1022 self.priority = priority
1022
1023
1023 @property
1024 @property
1024 def path(self):
1025 def path(self):
1025 return self.node.path
1026 return self.node.path
1026
1027
1027 def __repr__(self):
1028 def __repr__(self):
1028 return '<ReadmeMatch {} priority={}'.format(self.path, self.priority)
1029 return '<ReadmeMatch {} priority={}'.format(self.path, self.priority)
@@ -1,734 +1,744 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
3 # Copyright (C) 2011-2017 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
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=0755)
184 os.makedirs(create_path, mode=0755)
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 try:
257 try:
258 user = self._get_user(owner)
258 user = self._get_user(owner)
259 new_repo_group = RepoGroup()
259 new_repo_group = RepoGroup()
260 new_repo_group.user = user
260 new_repo_group.user = user
261 new_repo_group.group_description = group_description or group_name
261 new_repo_group.group_description = group_description or group_name
262 new_repo_group.parent_group = parent_group
262 new_repo_group.parent_group = parent_group
263 new_repo_group.group_name = group_name
263 new_repo_group.group_name = group_name
264 new_repo_group.personal = personal
264 new_repo_group.personal = personal
265
265
266 self.sa.add(new_repo_group)
266 self.sa.add(new_repo_group)
267
267
268 # create an ADMIN permission for owner except if we're super admin,
268 # create an ADMIN permission for owner except if we're super admin,
269 # later owner should go into the owner field of groups
269 # later owner should go into the owner field of groups
270 if not user.is_admin:
270 if not user.is_admin:
271 self.grant_user_permission(repo_group=new_repo_group,
271 self.grant_user_permission(repo_group=new_repo_group,
272 user=owner, perm='group.admin')
272 user=owner, perm='group.admin')
273
273
274 if parent_group and copy_permissions:
274 if parent_group and copy_permissions:
275 # copy permissions from parent
275 # copy permissions from parent
276 user_perms = UserRepoGroupToPerm.query() \
276 user_perms = UserRepoGroupToPerm.query() \
277 .filter(UserRepoGroupToPerm.group == parent_group).all()
277 .filter(UserRepoGroupToPerm.group == parent_group).all()
278
278
279 group_perms = UserGroupRepoGroupToPerm.query() \
279 group_perms = UserGroupRepoGroupToPerm.query() \
280 .filter(UserGroupRepoGroupToPerm.group == parent_group).all()
280 .filter(UserGroupRepoGroupToPerm.group == parent_group).all()
281
281
282 for perm in user_perms:
282 for perm in user_perms:
283 # don't copy over the permission for user who is creating
283 # don't copy over the permission for user who is creating
284 # this group, if he is not super admin he get's admin
284 # this group, if he is not super admin he get's admin
285 # permission set above
285 # permission set above
286 if perm.user != user or user.is_admin:
286 if perm.user != user or user.is_admin:
287 UserRepoGroupToPerm.create(
287 UserRepoGroupToPerm.create(
288 perm.user, new_repo_group, perm.permission)
288 perm.user, new_repo_group, perm.permission)
289
289
290 for perm in group_perms:
290 for perm in group_perms:
291 UserGroupRepoGroupToPerm.create(
291 UserGroupRepoGroupToPerm.create(
292 perm.users_group, new_repo_group, perm.permission)
292 perm.users_group, new_repo_group, perm.permission)
293 else:
293 else:
294 perm_obj = self._create_default_perms(new_repo_group)
294 perm_obj = self._create_default_perms(new_repo_group)
295 self.sa.add(perm_obj)
295 self.sa.add(perm_obj)
296
296
297 # now commit the changes, earlier so we are sure everything is in
297 # now commit the changes, earlier so we are sure everything is in
298 # the database.
298 # the database.
299 if commit_early:
299 if commit_early:
300 self.sa.commit()
300 self.sa.commit()
301 if not just_db:
301 if not just_db:
302 self._create_group(new_repo_group.group_name)
302 self._create_group(new_repo_group.group_name)
303
303
304 # trigger the post hook
304 # trigger the post hook
305 from rhodecode.lib.hooks_base import log_create_repository_group
305 from rhodecode.lib.hooks_base import log_create_repository_group
306 repo_group = RepoGroup.get_by_group_name(group_name)
306 repo_group = RepoGroup.get_by_group_name(group_name)
307 log_create_repository_group(
307 log_create_repository_group(
308 created_by=user.username, **repo_group.get_dict())
308 created_by=user.username, **repo_group.get_dict())
309
309
310 # Trigger create event.
310 # Trigger create event.
311 events.trigger(events.RepoGroupCreateEvent(repo_group))
311 events.trigger(events.RepoGroupCreateEvent(repo_group))
312
312
313 return new_repo_group
313 return new_repo_group
314 except Exception:
314 except Exception:
315 self.sa.rollback()
315 self.sa.rollback()
316 log.exception('Exception occurred when creating repository group, '
316 log.exception('Exception occurred when creating repository group, '
317 'doing cleanup...')
317 'doing cleanup...')
318 # rollback things manually !
318 # rollback things manually !
319 repo_group = RepoGroup.get_by_group_name(group_name)
319 repo_group = RepoGroup.get_by_group_name(group_name)
320 if repo_group:
320 if repo_group:
321 RepoGroup.delete(repo_group.group_id)
321 RepoGroup.delete(repo_group.group_id)
322 self.sa.commit()
322 self.sa.commit()
323 if cleanup_group:
323 if cleanup_group:
324 RepoGroupModel()._delete_filesystem_group(repo_group)
324 RepoGroupModel()._delete_filesystem_group(repo_group)
325 raise
325 raise
326
326
327 def update_permissions(
327 def update_permissions(
328 self, repo_group, perm_additions=None, perm_updates=None,
328 self, repo_group, perm_additions=None, perm_updates=None,
329 perm_deletions=None, recursive=None, check_perms=True,
329 perm_deletions=None, recursive=None, check_perms=True,
330 cur_user=None):
330 cur_user=None):
331 from rhodecode.model.repo import RepoModel
331 from rhodecode.model.repo import RepoModel
332 from rhodecode.lib.auth import HasUserGroupPermissionAny
332 from rhodecode.lib.auth import HasUserGroupPermissionAny
333
333
334 if not perm_additions:
334 if not perm_additions:
335 perm_additions = []
335 perm_additions = []
336 if not perm_updates:
336 if not perm_updates:
337 perm_updates = []
337 perm_updates = []
338 if not perm_deletions:
338 if not perm_deletions:
339 perm_deletions = []
339 perm_deletions = []
340
340
341 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
341 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
342
342
343 changes = {
343 changes = {
344 'added': [],
344 'added': [],
345 'updated': [],
345 'updated': [],
346 'deleted': []
346 'deleted': []
347 }
347 }
348
348
349 def _set_perm_user(obj, user, perm):
349 def _set_perm_user(obj, user, perm):
350 if isinstance(obj, RepoGroup):
350 if isinstance(obj, RepoGroup):
351 self.grant_user_permission(
351 self.grant_user_permission(
352 repo_group=obj, user=user, perm=perm)
352 repo_group=obj, user=user, perm=perm)
353 elif isinstance(obj, Repository):
353 elif isinstance(obj, Repository):
354 # private repos will not allow to change the default
354 # private repos will not allow to change the default
355 # permissions using recursive mode
355 # permissions using recursive mode
356 if obj.private and user == User.DEFAULT_USER:
356 if obj.private and user == User.DEFAULT_USER:
357 return
357 return
358
358
359 # we set group permission but we have to switch to repo
359 # we set group permission but we have to switch to repo
360 # permission
360 # permission
361 perm = perm.replace('group.', 'repository.')
361 perm = perm.replace('group.', 'repository.')
362 RepoModel().grant_user_permission(
362 RepoModel().grant_user_permission(
363 repo=obj, user=user, perm=perm)
363 repo=obj, user=user, perm=perm)
364
364
365 def _set_perm_group(obj, users_group, perm):
365 def _set_perm_group(obj, users_group, perm):
366 if isinstance(obj, RepoGroup):
366 if isinstance(obj, RepoGroup):
367 self.grant_user_group_permission(
367 self.grant_user_group_permission(
368 repo_group=obj, group_name=users_group, perm=perm)
368 repo_group=obj, group_name=users_group, perm=perm)
369 elif isinstance(obj, Repository):
369 elif isinstance(obj, Repository):
370 # we set group permission but we have to switch to repo
370 # we set group permission but we have to switch to repo
371 # permission
371 # permission
372 perm = perm.replace('group.', 'repository.')
372 perm = perm.replace('group.', 'repository.')
373 RepoModel().grant_user_group_permission(
373 RepoModel().grant_user_group_permission(
374 repo=obj, group_name=users_group, perm=perm)
374 repo=obj, group_name=users_group, perm=perm)
375
375
376 def _revoke_perm_user(obj, user):
376 def _revoke_perm_user(obj, user):
377 if isinstance(obj, RepoGroup):
377 if isinstance(obj, RepoGroup):
378 self.revoke_user_permission(repo_group=obj, user=user)
378 self.revoke_user_permission(repo_group=obj, user=user)
379 elif isinstance(obj, Repository):
379 elif isinstance(obj, Repository):
380 RepoModel().revoke_user_permission(repo=obj, user=user)
380 RepoModel().revoke_user_permission(repo=obj, user=user)
381
381
382 def _revoke_perm_group(obj, user_group):
382 def _revoke_perm_group(obj, user_group):
383 if isinstance(obj, RepoGroup):
383 if isinstance(obj, RepoGroup):
384 self.revoke_user_group_permission(
384 self.revoke_user_group_permission(
385 repo_group=obj, group_name=user_group)
385 repo_group=obj, group_name=user_group)
386 elif isinstance(obj, Repository):
386 elif isinstance(obj, Repository):
387 RepoModel().revoke_user_group_permission(
387 RepoModel().revoke_user_group_permission(
388 repo=obj, group_name=user_group)
388 repo=obj, group_name=user_group)
389
389
390 # start updates
390 # start updates
391 log.debug('Now updating permissions for %s in recursive mode:%s',
391 log.debug('Now updating permissions for %s in recursive mode:%s',
392 repo_group, recursive)
392 repo_group, recursive)
393
393
394 # initialize check function, we'll call that multiple times
394 # initialize check function, we'll call that multiple times
395 has_group_perm = HasUserGroupPermissionAny(*req_perms)
395 has_group_perm = HasUserGroupPermissionAny(*req_perms)
396
396
397 for obj in repo_group.recursive_groups_and_repos():
397 for obj in repo_group.recursive_groups_and_repos():
398 # iterated obj is an instance of a repos group or repository in
398 # iterated obj is an instance of a repos group or repository in
399 # that group, recursive option can be: none, repos, groups, all
399 # that group, recursive option can be: none, repos, groups, all
400 if recursive == 'all':
400 if recursive == 'all':
401 obj = obj
401 obj = obj
402 elif recursive == 'repos':
402 elif recursive == 'repos':
403 # skip groups, other than this one
403 # skip groups, other than this one
404 if isinstance(obj, RepoGroup) and not obj == repo_group:
404 if isinstance(obj, RepoGroup) and not obj == repo_group:
405 continue
405 continue
406 elif recursive == 'groups':
406 elif recursive == 'groups':
407 # skip repos
407 # skip repos
408 if isinstance(obj, Repository):
408 if isinstance(obj, Repository):
409 continue
409 continue
410 else: # recursive == 'none':
410 else: # recursive == 'none':
411 # DEFAULT option - don't apply to iterated objects
411 # DEFAULT option - don't apply to iterated objects
412 # also we do a break at the end of this loop. if we are not
412 # also we do a break at the end of this loop. if we are not
413 # in recursive mode
413 # in recursive mode
414 obj = repo_group
414 obj = repo_group
415
415
416 change_obj = obj.get_api_data()
416 change_obj = obj.get_api_data()
417
417
418 # update permissions
418 # update permissions
419 for member_id, perm, member_type in perm_updates:
419 for member_id, perm, member_type in perm_updates:
420 member_id = int(member_id)
420 member_id = int(member_id)
421 if member_type == 'user':
421 if member_type == 'user':
422 member_name = User.get(member_id).username
422 member_name = User.get(member_id).username
423 # this updates also current one if found
423 # this updates also current one if found
424 _set_perm_user(obj, user=member_id, perm=perm)
424 _set_perm_user(obj, user=member_id, perm=perm)
425 else: # set for user group
425 else: # set for user group
426 member_name = UserGroup.get(member_id).users_group_name
426 member_name = UserGroup.get(member_id).users_group_name
427 if not check_perms or has_group_perm(member_name,
427 if not check_perms or has_group_perm(member_name,
428 user=cur_user):
428 user=cur_user):
429 _set_perm_group(obj, users_group=member_id, perm=perm)
429 _set_perm_group(obj, users_group=member_id, perm=perm)
430
430
431 changes['updated'].append(
431 changes['updated'].append(
432 {'change_obj': change_obj, 'type': member_type,
432 {'change_obj': change_obj, 'type': member_type,
433 'id': member_id, 'name': member_name, 'new_perm': perm})
433 'id': member_id, 'name': member_name, 'new_perm': perm})
434
434
435 # set new permissions
435 # set new permissions
436 for member_id, perm, member_type in perm_additions:
436 for member_id, perm, member_type in perm_additions:
437 member_id = int(member_id)
437 member_id = int(member_id)
438 if member_type == 'user':
438 if member_type == 'user':
439 member_name = User.get(member_id).username
439 member_name = User.get(member_id).username
440 _set_perm_user(obj, user=member_id, perm=perm)
440 _set_perm_user(obj, user=member_id, perm=perm)
441 else: # set for user group
441 else: # set for user group
442 # check if we have permissions to alter this usergroup
442 # check if we have permissions to alter this usergroup
443 member_name = UserGroup.get(member_id).users_group_name
443 member_name = UserGroup.get(member_id).users_group_name
444 if not check_perms or has_group_perm(member_name,
444 if not check_perms or has_group_perm(member_name,
445 user=cur_user):
445 user=cur_user):
446 _set_perm_group(obj, users_group=member_id, perm=perm)
446 _set_perm_group(obj, users_group=member_id, perm=perm)
447
447
448 changes['added'].append(
448 changes['added'].append(
449 {'change_obj': change_obj, 'type': member_type,
449 {'change_obj': change_obj, 'type': member_type,
450 'id': member_id, 'name': member_name, 'new_perm': perm})
450 'id': member_id, 'name': member_name, 'new_perm': perm})
451
451
452 # delete permissions
452 # delete permissions
453 for member_id, perm, member_type in perm_deletions:
453 for member_id, perm, member_type in perm_deletions:
454 member_id = int(member_id)
454 member_id = int(member_id)
455 if member_type == 'user':
455 if member_type == 'user':
456 member_name = User.get(member_id).username
456 member_name = User.get(member_id).username
457 _revoke_perm_user(obj, user=member_id)
457 _revoke_perm_user(obj, user=member_id)
458 else: # set for user group
458 else: # set for user group
459 # check if we have permissions to alter this usergroup
459 # check if we have permissions to alter this usergroup
460 member_name = UserGroup.get(member_id).users_group_name
460 member_name = UserGroup.get(member_id).users_group_name
461 if not check_perms or has_group_perm(member_name,
461 if not check_perms or has_group_perm(member_name,
462 user=cur_user):
462 user=cur_user):
463 _revoke_perm_group(obj, user_group=member_id)
463 _revoke_perm_group(obj, user_group=member_id)
464
464
465 changes['deleted'].append(
465 changes['deleted'].append(
466 {'change_obj': change_obj, 'type': member_type,
466 {'change_obj': change_obj, 'type': member_type,
467 'id': member_id, 'name': member_name, 'new_perm': perm})
467 'id': member_id, 'name': member_name, 'new_perm': perm})
468
468
469 # if it's not recursive call for all,repos,groups
469 # if it's not recursive call for all,repos,groups
470 # break the loop and don't proceed with other changes
470 # break the loop and don't proceed with other changes
471 if recursive not in ['all', 'repos', 'groups']:
471 if recursive not in ['all', 'repos', 'groups']:
472 break
472 break
473
473
474 return changes
474 return changes
475
475
476 def update(self, repo_group, form_data):
476 def update(self, repo_group, form_data):
477 try:
477 try:
478 repo_group = self._get_repo_group(repo_group)
478 repo_group = self._get_repo_group(repo_group)
479 old_path = repo_group.full_path
479 old_path = repo_group.full_path
480
480
481 # change properties
481 # change properties
482 if 'group_description' in form_data:
482 if 'group_description' in form_data:
483 repo_group.group_description = form_data['group_description']
483 repo_group.group_description = form_data['group_description']
484
484
485 if 'enable_locking' in form_data:
485 if 'enable_locking' in form_data:
486 repo_group.enable_locking = form_data['enable_locking']
486 repo_group.enable_locking = form_data['enable_locking']
487
487
488 if 'group_parent_id' in form_data:
488 if 'group_parent_id' in form_data:
489 parent_group = (
489 parent_group = (
490 self._get_repo_group(form_data['group_parent_id']))
490 self._get_repo_group(form_data['group_parent_id']))
491 repo_group.group_parent_id = (
491 repo_group.group_parent_id = (
492 parent_group.group_id if parent_group else None)
492 parent_group.group_id if parent_group else None)
493 repo_group.parent_group = parent_group
493 repo_group.parent_group = parent_group
494
494
495 # mikhail: to update the full_path, we have to explicitly
495 # mikhail: to update the full_path, we have to explicitly
496 # update group_name
496 # update group_name
497 group_name = form_data.get('group_name', repo_group.name)
497 group_name = form_data.get('group_name', repo_group.name)
498 repo_group.group_name = repo_group.get_new_name(group_name)
498 repo_group.group_name = repo_group.get_new_name(group_name)
499
499
500 new_path = repo_group.full_path
500 new_path = repo_group.full_path
501
501
502 if 'user' in form_data:
502 if 'user' in form_data:
503 repo_group.user = User.get_by_username(form_data['user'])
503 repo_group.user = User.get_by_username(form_data['user'])
504
504 repo_group.updated_on = datetime.datetime.now()
505 self.sa.add(repo_group)
505 self.sa.add(repo_group)
506
506
507 # iterate over all members of this groups and do fixes
507 # iterate over all members of this groups and do fixes
508 # set locking if given
508 # set locking if given
509 # if obj is a repoGroup also fix the name of the group according
509 # if obj is a repoGroup also fix the name of the group according
510 # to the parent
510 # to the parent
511 # if obj is a Repo fix it's name
511 # if obj is a Repo fix it's name
512 # this can be potentially heavy operation
512 # this can be potentially heavy operation
513 for obj in repo_group.recursive_groups_and_repos():
513 for obj in repo_group.recursive_groups_and_repos():
514 # set the value from it's parent
514 # set the value from it's parent
515 obj.enable_locking = repo_group.enable_locking
515 obj.enable_locking = repo_group.enable_locking
516 if isinstance(obj, RepoGroup):
516 if isinstance(obj, RepoGroup):
517 new_name = obj.get_new_name(obj.name)
517 new_name = obj.get_new_name(obj.name)
518 log.debug('Fixing group %s to new name %s',
518 log.debug('Fixing group %s to new name %s',
519 obj.group_name, new_name)
519 obj.group_name, new_name)
520 obj.group_name = new_name
520 obj.group_name = new_name
521 obj.updated_on = datetime.datetime.now()
521 elif isinstance(obj, Repository):
522 elif isinstance(obj, Repository):
522 # we need to get all repositories from this new group and
523 # we need to get all repositories from this new group and
523 # rename them accordingly to new group path
524 # rename them accordingly to new group path
524 new_name = obj.get_new_name(obj.just_name)
525 new_name = obj.get_new_name(obj.just_name)
525 log.debug('Fixing repo %s to new name %s',
526 log.debug('Fixing repo %s to new name %s',
526 obj.repo_name, new_name)
527 obj.repo_name, new_name)
527 obj.repo_name = new_name
528 obj.repo_name = new_name
529 obj.updated_on = datetime.datetime.now()
528 self.sa.add(obj)
530 self.sa.add(obj)
529
531
530 self._rename_group(old_path, new_path)
532 self._rename_group(old_path, new_path)
531
533
532 # Trigger update event.
534 # Trigger update event.
533 events.trigger(events.RepoGroupUpdateEvent(repo_group))
535 events.trigger(events.RepoGroupUpdateEvent(repo_group))
534
536
535 return repo_group
537 return repo_group
536 except Exception:
538 except Exception:
537 log.error(traceback.format_exc())
539 log.error(traceback.format_exc())
538 raise
540 raise
539
541
540 def delete(self, repo_group, force_delete=False, fs_remove=True):
542 def delete(self, repo_group, force_delete=False, fs_remove=True):
541 repo_group = self._get_repo_group(repo_group)
543 repo_group = self._get_repo_group(repo_group)
542 if not repo_group:
544 if not repo_group:
543 return False
545 return False
544 try:
546 try:
545 self.sa.delete(repo_group)
547 self.sa.delete(repo_group)
546 if fs_remove:
548 if fs_remove:
547 self._delete_filesystem_group(repo_group, force_delete)
549 self._delete_filesystem_group(repo_group, force_delete)
548 else:
550 else:
549 log.debug('skipping removal from filesystem')
551 log.debug('skipping removal from filesystem')
550
552
551 # Trigger delete event.
553 # Trigger delete event.
552 events.trigger(events.RepoGroupDeleteEvent(repo_group))
554 events.trigger(events.RepoGroupDeleteEvent(repo_group))
553 return True
555 return True
554
556
555 except Exception:
557 except Exception:
556 log.error('Error removing repo_group %s', repo_group)
558 log.error('Error removing repo_group %s', repo_group)
557 raise
559 raise
558
560
559 def grant_user_permission(self, repo_group, user, perm):
561 def grant_user_permission(self, repo_group, user, perm):
560 """
562 """
561 Grant permission for user on given repository group, or update
563 Grant permission for user on given repository group, or update
562 existing one if found
564 existing one if found
563
565
564 :param repo_group: Instance of RepoGroup, repositories_group_id,
566 :param repo_group: Instance of RepoGroup, repositories_group_id,
565 or repositories_group name
567 or repositories_group name
566 :param user: Instance of User, user_id or username
568 :param user: Instance of User, user_id or username
567 :param perm: Instance of Permission, or permission_name
569 :param perm: Instance of Permission, or permission_name
568 """
570 """
569
571
570 repo_group = self._get_repo_group(repo_group)
572 repo_group = self._get_repo_group(repo_group)
571 user = self._get_user(user)
573 user = self._get_user(user)
572 permission = self._get_perm(perm)
574 permission = self._get_perm(perm)
573
575
574 # check if we have that permission already
576 # check if we have that permission already
575 obj = self.sa.query(UserRepoGroupToPerm)\
577 obj = self.sa.query(UserRepoGroupToPerm)\
576 .filter(UserRepoGroupToPerm.user == user)\
578 .filter(UserRepoGroupToPerm.user == user)\
577 .filter(UserRepoGroupToPerm.group == repo_group)\
579 .filter(UserRepoGroupToPerm.group == repo_group)\
578 .scalar()
580 .scalar()
579 if obj is None:
581 if obj is None:
580 # create new !
582 # create new !
581 obj = UserRepoGroupToPerm()
583 obj = UserRepoGroupToPerm()
582 obj.group = repo_group
584 obj.group = repo_group
583 obj.user = user
585 obj.user = user
584 obj.permission = permission
586 obj.permission = permission
585 self.sa.add(obj)
587 self.sa.add(obj)
586 log.debug('Granted perm %s to %s on %s', perm, user, repo_group)
588 log.debug('Granted perm %s to %s on %s', perm, user, repo_group)
587 action_logger_generic(
589 action_logger_generic(
588 'granted permission: {} to user: {} on repogroup: {}'.format(
590 'granted permission: {} to user: {} on repogroup: {}'.format(
589 perm, user, repo_group), namespace='security.repogroup')
591 perm, user, repo_group), namespace='security.repogroup')
590 return obj
592 return obj
591
593
592 def revoke_user_permission(self, repo_group, user):
594 def revoke_user_permission(self, repo_group, user):
593 """
595 """
594 Revoke permission for user on given repository group
596 Revoke permission for user on given repository group
595
597
596 :param repo_group: Instance of RepoGroup, repositories_group_id,
598 :param repo_group: Instance of RepoGroup, repositories_group_id,
597 or repositories_group name
599 or repositories_group name
598 :param user: Instance of User, user_id or username
600 :param user: Instance of User, user_id or username
599 """
601 """
600
602
601 repo_group = self._get_repo_group(repo_group)
603 repo_group = self._get_repo_group(repo_group)
602 user = self._get_user(user)
604 user = self._get_user(user)
603
605
604 obj = self.sa.query(UserRepoGroupToPerm)\
606 obj = self.sa.query(UserRepoGroupToPerm)\
605 .filter(UserRepoGroupToPerm.user == user)\
607 .filter(UserRepoGroupToPerm.user == user)\
606 .filter(UserRepoGroupToPerm.group == repo_group)\
608 .filter(UserRepoGroupToPerm.group == repo_group)\
607 .scalar()
609 .scalar()
608 if obj:
610 if obj:
609 self.sa.delete(obj)
611 self.sa.delete(obj)
610 log.debug('Revoked perm on %s on %s', repo_group, user)
612 log.debug('Revoked perm on %s on %s', repo_group, user)
611 action_logger_generic(
613 action_logger_generic(
612 'revoked permission from user: {} on repogroup: {}'.format(
614 'revoked permission from user: {} on repogroup: {}'.format(
613 user, repo_group), namespace='security.repogroup')
615 user, repo_group), namespace='security.repogroup')
614
616
615 def grant_user_group_permission(self, repo_group, group_name, perm):
617 def grant_user_group_permission(self, repo_group, group_name, perm):
616 """
618 """
617 Grant permission for user group on given repository group, or update
619 Grant permission for user group on given repository group, or update
618 existing one if found
620 existing one if found
619
621
620 :param repo_group: Instance of RepoGroup, repositories_group_id,
622 :param repo_group: Instance of RepoGroup, repositories_group_id,
621 or repositories_group name
623 or repositories_group name
622 :param group_name: Instance of UserGroup, users_group_id,
624 :param group_name: Instance of UserGroup, users_group_id,
623 or user group name
625 or user group name
624 :param perm: Instance of Permission, or permission_name
626 :param perm: Instance of Permission, or permission_name
625 """
627 """
626 repo_group = self._get_repo_group(repo_group)
628 repo_group = self._get_repo_group(repo_group)
627 group_name = self._get_user_group(group_name)
629 group_name = self._get_user_group(group_name)
628 permission = self._get_perm(perm)
630 permission = self._get_perm(perm)
629
631
630 # check if we have that permission already
632 # check if we have that permission already
631 obj = self.sa.query(UserGroupRepoGroupToPerm)\
633 obj = self.sa.query(UserGroupRepoGroupToPerm)\
632 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
634 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
633 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
635 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
634 .scalar()
636 .scalar()
635
637
636 if obj is None:
638 if obj is None:
637 # create new
639 # create new
638 obj = UserGroupRepoGroupToPerm()
640 obj = UserGroupRepoGroupToPerm()
639
641
640 obj.group = repo_group
642 obj.group = repo_group
641 obj.users_group = group_name
643 obj.users_group = group_name
642 obj.permission = permission
644 obj.permission = permission
643 self.sa.add(obj)
645 self.sa.add(obj)
644 log.debug('Granted perm %s to %s on %s', perm, group_name, repo_group)
646 log.debug('Granted perm %s to %s on %s', perm, group_name, repo_group)
645 action_logger_generic(
647 action_logger_generic(
646 'granted permission: {} to usergroup: {} on repogroup: {}'.format(
648 'granted permission: {} to usergroup: {} on repogroup: {}'.format(
647 perm, group_name, repo_group), namespace='security.repogroup')
649 perm, group_name, repo_group), namespace='security.repogroup')
648 return obj
650 return obj
649
651
650 def revoke_user_group_permission(self, repo_group, group_name):
652 def revoke_user_group_permission(self, repo_group, group_name):
651 """
653 """
652 Revoke permission for user group on given repository group
654 Revoke permission for user group on given repository group
653
655
654 :param repo_group: Instance of RepoGroup, repositories_group_id,
656 :param repo_group: Instance of RepoGroup, repositories_group_id,
655 or repositories_group name
657 or repositories_group name
656 :param group_name: Instance of UserGroup, users_group_id,
658 :param group_name: Instance of UserGroup, users_group_id,
657 or user group name
659 or user group name
658 """
660 """
659 repo_group = self._get_repo_group(repo_group)
661 repo_group = self._get_repo_group(repo_group)
660 group_name = self._get_user_group(group_name)
662 group_name = self._get_user_group(group_name)
661
663
662 obj = self.sa.query(UserGroupRepoGroupToPerm)\
664 obj = self.sa.query(UserGroupRepoGroupToPerm)\
663 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
665 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
664 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
666 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
665 .scalar()
667 .scalar()
666 if obj:
668 if obj:
667 self.sa.delete(obj)
669 self.sa.delete(obj)
668 log.debug('Revoked perm to %s on %s', repo_group, group_name)
670 log.debug('Revoked perm to %s on %s', repo_group, group_name)
669 action_logger_generic(
671 action_logger_generic(
670 'revoked permission from usergroup: {} on repogroup: {}'.format(
672 'revoked permission from usergroup: {} on repogroup: {}'.format(
671 group_name, repo_group), namespace='security.repogroup')
673 group_name, repo_group), namespace='security.repogroup')
672
674
673 def get_repo_groups_as_dict(self, repo_group_list=None, admin=False,
675 def get_repo_groups_as_dict(self, repo_group_list=None, admin=False,
674 super_user_actions=False):
676 super_user_actions=False):
675
677
676 from pyramid.threadlocal import get_current_request
678 from pyramid.threadlocal import get_current_request
677 _render = get_current_request().get_partial_renderer(
679 _render = get_current_request().get_partial_renderer(
678 'data_table/_dt_elements.mako')
680 'data_table/_dt_elements.mako')
679 c = _render.get_call_context()
681 c = _render.get_call_context()
680 h = _render.get_helpers()
682 h = _render.get_helpers()
681
683
682 def quick_menu(repo_group_name):
684 def quick_menu(repo_group_name):
683 return _render('quick_repo_group_menu', repo_group_name)
685 return _render('quick_repo_group_menu', repo_group_name)
684
686
685 def repo_group_lnk(repo_group_name):
687 def repo_group_lnk(repo_group_name):
686 return _render('repo_group_name', repo_group_name)
688 return _render('repo_group_name', repo_group_name)
687
689
690 def last_change(last_change):
691 if admin and isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
692 last_change = last_change + datetime.timedelta(seconds=
693 (datetime.datetime.now() - datetime.datetime.utcnow()).seconds)
694 return _render("last_change", last_change)
695
688 def desc(desc, personal):
696 def desc(desc, personal):
689 prefix = h.escaped_stylize(u'[personal] ') if personal else ''
697 prefix = h.escaped_stylize(u'[personal] ') if personal else ''
690
698
691 if c.visual.stylify_metatags:
699 if c.visual.stylify_metatags:
692 desc = h.urlify_text(prefix + h.escaped_stylize(desc))
700 desc = h.urlify_text(prefix + h.escaped_stylize(desc))
693 else:
701 else:
694 desc = h.urlify_text(prefix + h.html_escape(desc))
702 desc = h.urlify_text(prefix + h.html_escape(desc))
695
703
696 return _render('repo_group_desc', desc)
704 return _render('repo_group_desc', desc)
697
705
698 def repo_group_actions(repo_group_id, repo_group_name, gr_count):
706 def repo_group_actions(repo_group_id, repo_group_name, gr_count):
699 return _render(
707 return _render(
700 'repo_group_actions', repo_group_id, repo_group_name, gr_count)
708 'repo_group_actions', repo_group_id, repo_group_name, gr_count)
701
709
702 def repo_group_name(repo_group_name, children_groups):
710 def repo_group_name(repo_group_name, children_groups):
703 return _render("repo_group_name", repo_group_name, children_groups)
711 return _render("repo_group_name", repo_group_name, children_groups)
704
712
705 def user_profile(username):
713 def user_profile(username):
706 return _render('user_profile', username)
714 return _render('user_profile', username)
707
715
708 repo_group_data = []
716 repo_group_data = []
709 for group in repo_group_list:
717 for group in repo_group_list:
710
718
711 row = {
719 row = {
712 "menu": quick_menu(group.group_name),
720 "menu": quick_menu(group.group_name),
713 "name": repo_group_lnk(group.group_name),
721 "name": repo_group_lnk(group.group_name),
714 "name_raw": group.group_name,
722 "name_raw": group.group_name,
723 "last_change": last_change(group.last_db_change),
724 "last_change_raw": datetime_to_time(group.last_db_change),
715 "desc": desc(group.description_safe, group.personal),
725 "desc": desc(group.description_safe, group.personal),
716 "top_level_repos": 0,
726 "top_level_repos": 0,
717 "owner": user_profile(group.user.username)
727 "owner": user_profile(group.user.username)
718 }
728 }
719 if admin:
729 if admin:
720 repo_count = group.repositories.count()
730 repo_count = group.repositories.count()
721 children_groups = map(
731 children_groups = map(
722 h.safe_unicode,
732 h.safe_unicode,
723 itertools.chain((g.name for g in group.parents),
733 itertools.chain((g.name for g in group.parents),
724 (x.name for x in [group])))
734 (x.name for x in [group])))
725 row.update({
735 row.update({
726 "action": repo_group_actions(
736 "action": repo_group_actions(
727 group.group_id, group.group_name, repo_count),
737 group.group_id, group.group_name, repo_count),
728 "top_level_repos": repo_count,
738 "top_level_repos": repo_count,
729 "name": repo_group_name(group.group_name, children_groups),
739 "name": repo_group_name(group.group_name, children_groups),
730
740
731 })
741 })
732 repo_group_data.append(row)
742 repo_group_data.append(row)
733
743
734 return repo_group_data
744 return repo_group_data
@@ -1,94 +1,97 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Repository groups administration')}
5 ${_('Repository groups administration')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
13 ${h.link_to(_('Admin'),h.route_path('admin_home'))} &raquo; <span id="repo_group_count">0</span> ${_('repository groups')}
13 ${h.link_to(_('Admin'),h.route_path('admin_home'))} &raquo; <span id="repo_group_count">0</span> ${_('repository groups')}
14 </%def>
14 </%def>
15
15
16 <%def name="menu_bar_nav()">
16 <%def name="menu_bar_nav()">
17 ${self.menu_items(active='admin')}
17 ${self.menu_items(active='admin')}
18 </%def>
18 </%def>
19
19
20 <%def name="main()">
20 <%def name="main()">
21 <div class="box">
21 <div class="box">
22 <div class="title">
22 <div class="title">
23 ${self.breadcrumbs()}
23 ${self.breadcrumbs()}
24 <ul class="links">
24 <ul class="links">
25 %if h.HasPermissionAny('hg.admin','hg.repogroup.create.true')():
25 %if h.HasPermissionAny('hg.admin','hg.repogroup.create.true')():
26 <li>
26 <li>
27 <a href="${h.url('new_repo_group')}" class="btn btn-small btn-success">${_(u'Add Repository Group')}</a>
27 <a href="${h.url('new_repo_group')}" class="btn btn-small btn-success">${_(u'Add Repository Group')}</a>
28 </li>
28 </li>
29 %endif
29 %endif
30 </ul>
30 </ul>
31 </div>
31 </div>
32 <div id="repos_list_wrap">
32 <div id="repos_list_wrap">
33 <table id="group_list_table" class="display"></table>
33 <table id="group_list_table" class="display"></table>
34 </div>
34 </div>
35 </div>
35 </div>
36
36
37 <script>
37 <script>
38 $(document).ready(function() {
38 $(document).ready(function() {
39
39
40 var get_datatable_count = function(){
40 var get_datatable_count = function(){
41 var api = $('#group_list_table').dataTable().api();
41 var api = $('#group_list_table').dataTable().api();
42 $('#repo_group_count').text(api.page.info().recordsDisplay);
42 $('#repo_group_count').text(api.page.info().recordsDisplay);
43 };
43 };
44
44
45 // repo group list
45 // repo group list
46 $('#group_list_table').DataTable({
46 $('#group_list_table').DataTable({
47 data: ${c.data|n},
47 data: ${c.data|n},
48 dom: 'rtp',
48 dom: 'rtp',
49 pageLength: ${c.visual.admin_grid_items},
49 pageLength: ${c.visual.admin_grid_items},
50 order: [[ 0, "asc" ]],
50 order: [[ 0, "asc" ]],
51 columns: [
51 columns: [
52 { data: {"_": "name",
52 { data: {"_": "name",
53 "sort": "name_raw"}, title: "${_('Name')}", className: "td-componentname" },
53 "sort": "name_raw"}, title: "${_('Name')}", className: "td-componentname" },
54 { data: 'menu', "bSortable": false, className: "quick_repo_menu" },
54 { data: 'menu', "bSortable": false, className: "quick_repo_menu" },
55 { data: {"_": "desc",
55 { data: {"_": "desc",
56 "sort": "desc"}, title: "${_('Description')}", className: "td-description" },
56 "sort": "desc"}, title: "${_('Description')}", className: "td-description" },
57 { data: {"_": "last_change",
58 "sort": "last_change_raw",
59 "type": Number}, title: "${_('Last Change')}", className: "td-time" },
57 { data: {"_": "top_level_repos",
60 { data: {"_": "top_level_repos",
58 "sort": "top_level_repos"}, title: "${_('Number of top level repositories')}" },
61 "sort": "top_level_repos"}, title: "${_('Number of top level repositories')}" },
59 { data: {"_": "owner",
62 { data: {"_": "owner",
60 "sort": "owner"}, title: "${_('Owner')}", className: "td-user" },
63 "sort": "owner"}, title: "${_('Owner')}", className: "td-user" },
61 { data: {"_": "action",
64 { data: {"_": "action",
62 "sort": "action"}, title: "${_('Action')}", className: "td-action" }
65 "sort": "action"}, title: "${_('Action')}", className: "td-action" }
63 ],
66 ],
64 language: {
67 language: {
65 paginate: DEFAULT_GRID_PAGINATION,
68 paginate: DEFAULT_GRID_PAGINATION,
66 emptyTable: _gettext("No repository groups available yet.")
69 emptyTable: _gettext("No repository groups available yet.")
67 },
70 },
68 "initComplete": function( settings, json ) {
71 "initComplete": function( settings, json ) {
69 get_datatable_count();
72 get_datatable_count();
70 quick_repo_menu();
73 quick_repo_menu();
71 }
74 }
72 });
75 });
73
76
74 // update the counter when doing search
77 // update the counter when doing search
75 $('#group_list_table').on( 'search.dt', function (e,settings) {
78 $('#group_list_table').on( 'search.dt', function (e,settings) {
76 get_datatable_count();
79 get_datatable_count();
77 });
80 });
78
81
79 // filter, filter both grids
82 // filter, filter both grids
80 $('#q_filter').on( 'keyup', function () {
83 $('#q_filter').on( 'keyup', function () {
81
84
82 var repo_group_api = $('#group_list_table').dataTable().api();
85 var repo_group_api = $('#group_list_table').dataTable().api();
83 repo_group_api
86 repo_group_api
84 .columns(0)
87 .columns(0)
85 .search(this.value)
88 .search(this.value)
86 .draw();
89 .draw();
87 });
90 });
88
91
89 // refilter table if page load via back button
92 // refilter table if page load via back button
90 $("#q_filter").trigger('keyup');
93 $("#q_filter").trigger('keyup');
91 });
94 });
92 </script>
95 </script>
93 </%def>
96 </%def>
94
97
@@ -1,170 +1,173 b''
1 <%inherit file="/base/base.mako"/>
1 <%inherit file="/base/base.mako"/>
2
2
3 <%def name="main()">
3 <%def name="main()">
4 <div class="box">
4 <div class="box">
5 <!-- box / title -->
5 <!-- box / title -->
6 <div class="title">
6 <div class="title">
7 <div class="block-left breadcrumbs">
7 <div class="block-left breadcrumbs">
8 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
8 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
9 ${self.breadcrumbs()}
9 ${self.breadcrumbs()}
10 <span id="match_container" style="display:none">&raquo; <span id="match_count">0</span> ${_('matches')}</span>
10 <span id="match_container" style="display:none">&raquo; <span id="match_count">0</span> ${_('matches')}</span>
11 </div>
11 </div>
12 %if c.rhodecode_user.username != h.DEFAULT_USER:
12 %if c.rhodecode_user.username != h.DEFAULT_USER:
13 <div class="block-right">
13 <div class="block-right">
14 <%
14 <%
15 is_admin = h.HasPermissionAny('hg.admin')('can create repos index page')
15 is_admin = h.HasPermissionAny('hg.admin')('can create repos index page')
16 create_repo = h.HasPermissionAny('hg.create.repository')('can create repository index page')
16 create_repo = h.HasPermissionAny('hg.create.repository')('can create repository index page')
17 create_repo_group = h.HasPermissionAny('hg.repogroup.create.true')('can create repository groups index page')
17 create_repo_group = h.HasPermissionAny('hg.repogroup.create.true')('can create repository groups index page')
18 create_user_group = h.HasPermissionAny('hg.usergroup.create.true')('can create user groups index page')
18 create_user_group = h.HasPermissionAny('hg.usergroup.create.true')('can create user groups index page')
19
19
20 gr_name = c.repo_group.group_name if c.repo_group else None
20 gr_name = c.repo_group.group_name if c.repo_group else None
21 # create repositories with write permission on group is set to true
21 # create repositories with write permission on group is set to true
22 create_on_write = h.HasPermissionAny('hg.create.write_on_repogroup.true')()
22 create_on_write = h.HasPermissionAny('hg.create.write_on_repogroup.true')()
23 group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page')
23 group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page')
24 group_write = h.HasRepoGroupPermissionAny('group.write')(gr_name, 'can write into group index page')
24 group_write = h.HasRepoGroupPermissionAny('group.write')(gr_name, 'can write into group index page')
25 %>
25 %>
26
26
27 %if not c.repo_group:
27 %if not c.repo_group:
28 ## no repository group context here
28 ## no repository group context here
29 %if is_admin or create_repo:
29 %if is_admin or create_repo:
30 <a href="${h.url('new_repo')}" class="btn btn-small btn-success btn-primary">${_('Add Repository')}</a>
30 <a href="${h.url('new_repo')}" class="btn btn-small btn-success btn-primary">${_('Add Repository')}</a>
31 %endif
31 %endif
32
32
33 %if is_admin or create_repo_group:
33 %if is_admin or create_repo_group:
34 <a href="${h.url('new_repo_group')}" class="btn btn-small btn-default">${_(u'Add Repository Group')}</a>
34 <a href="${h.url('new_repo_group')}" class="btn btn-small btn-default">${_(u'Add Repository Group')}</a>
35 %endif
35 %endif
36 %else:
36 %else:
37 ##we're inside other repository group other terms apply
37 ##we're inside other repository group other terms apply
38 %if is_admin or group_admin or (group_write and create_on_write):
38 %if is_admin or group_admin or (group_write and create_on_write):
39 <a href="${h.url('new_repo',parent_group=c.repo_group.group_id)}" class="btn btn-small btn-success btn-primary">${_('Add Repository')}</a>
39 <a href="${h.url('new_repo',parent_group=c.repo_group.group_id)}" class="btn btn-small btn-success btn-primary">${_('Add Repository')}</a>
40 %endif
40 %endif
41 %if is_admin or group_admin:
41 %if is_admin or group_admin:
42 <a href="${h.url('new_repo_group', parent_group=c.repo_group.group_id)}" class="btn btn-small btn-default">${_(u'Add Repository Group')}</a>
42 <a href="${h.url('new_repo_group', parent_group=c.repo_group.group_id)}" class="btn btn-small btn-default">${_(u'Add Repository Group')}</a>
43 %endif
43 %endif
44 %if is_admin or group_admin:
44 %if is_admin or group_admin:
45 <a href="${h.url('edit_repo_group',group_name=c.repo_group.group_name)}" title="${_('You have admin right to this group, and can edit it')}" class="btn btn-small btn-primary">${_('Edit Repository Group')}</a>
45 <a href="${h.url('edit_repo_group',group_name=c.repo_group.group_name)}" title="${_('You have admin right to this group, and can edit it')}" class="btn btn-small btn-primary">${_('Edit Repository Group')}</a>
46 %endif
46 %endif
47 %endif
47 %endif
48 </div>
48 </div>
49 %endif
49 %endif
50 </div>
50 </div>
51 <!-- end box / title -->
51 <!-- end box / title -->
52 <div class="table">
52 <div class="table">
53 <div id="groups_list_wrap">
53 <div id="groups_list_wrap">
54 <table id="group_list_table" class="display"></table>
54 <table id="group_list_table" class="display"></table>
55 </div>
55 </div>
56 </div>
56 </div>
57
57
58 <div class="table">
58 <div class="table">
59 <div id="repos_list_wrap">
59 <div id="repos_list_wrap">
60 <table id="repo_list_table" class="display"></table>
60 <table id="repo_list_table" class="display"></table>
61 </div>
61 </div>
62 </div>
62 </div>
63 </div>
63 </div>
64 <script>
64 <script>
65 $(document).ready(function() {
65 $(document).ready(function() {
66
66
67 var get_datatable_count = function() {
67 var get_datatable_count = function() {
68 var api = $('#repo_list_table').dataTable().api();
68 var api = $('#repo_list_table').dataTable().api();
69 var pageInfo = api.page.info();
69 var pageInfo = api.page.info();
70 var repos = pageInfo.recordsDisplay;
70 var repos = pageInfo.recordsDisplay;
71 var reposTotal = pageInfo.recordsTotal;
71 var reposTotal = pageInfo.recordsTotal;
72
72
73 api = $('#group_list_table').dataTable().api();
73 api = $('#group_list_table').dataTable().api();
74 pageInfo = api.page.info();
74 pageInfo = api.page.info();
75 var repoGroups = pageInfo.recordsDisplay;
75 var repoGroups = pageInfo.recordsDisplay;
76 var repoGroupsTotal = pageInfo.recordsTotal;
76 var repoGroupsTotal = pageInfo.recordsTotal;
77
77
78 if (repoGroups !== repoGroupsTotal) {
78 if (repoGroups !== repoGroupsTotal) {
79 $('#match_count').text(repos+repoGroups);
79 $('#match_count').text(repos+repoGroups);
80 }
80 }
81 if (repos !== reposTotal) {
81 if (repos !== reposTotal) {
82 $('#match_container').show();
82 $('#match_container').show();
83 }
83 }
84 if ($('#q_filter').val() === '') {
84 if ($('#q_filter').val() === '') {
85 $('#match_container').hide();
85 $('#match_container').hide();
86 }
86 }
87 };
87 };
88
88
89 // repo group list
89 // repo group list
90 $('#group_list_table').DataTable({
90 $('#group_list_table').DataTable({
91 data: ${c.repo_groups_data|n},
91 data: ${c.repo_groups_data|n},
92 dom: 'rtp',
92 dom: 'rtp',
93 pageLength: ${c.visual.dashboard_items},
93 pageLength: ${c.visual.dashboard_items},
94 order: [[ 0, "asc" ]],
94 order: [[ 0, "asc" ]],
95 columns: [
95 columns: [
96 { data: {"_": "name",
96 { data: {"_": "name",
97 "sort": "name_raw"}, title: "${_('Name')}", className: "td-componentname" },
97 "sort": "name_raw"}, title: "${_('Name')}", className: "td-componentname" },
98 { data: 'menu', "bSortable": false, className: "quick_repo_menu" },
98 { data: 'menu', "bSortable": false, className: "quick_repo_menu" },
99 { data: {"_": "desc",
99 { data: {"_": "desc",
100 "sort": "desc"}, title: "${_('Description')}", className: "td-description" },
100 "sort": "desc"}, title: "${_('Description')}", className: "td-description" },
101 { data: {"_": "last_change",
102 "sort": "last_change_raw",
103 "type": Number}, title: "${_('Last Change')}", className: "td-time" },
101 { data: {"_": "owner",
104 { data: {"_": "owner",
102 "sort": "owner"}, title: "${_('Owner')}", className: "td-user" }
105 "sort": "owner"}, title: "${_('Owner')}", className: "td-user" }
103 ],
106 ],
104 language: {
107 language: {
105 paginate: DEFAULT_GRID_PAGINATION,
108 paginate: DEFAULT_GRID_PAGINATION,
106 emptyTable: _gettext("No repository groups available yet.")
109 emptyTable: _gettext("No repository groups available yet.")
107 },
110 },
108 "drawCallback": function( settings, json ) {
111 "drawCallback": function( settings, json ) {
109 timeagoActivate();
112 timeagoActivate();
110 quick_repo_menu();
113 quick_repo_menu();
111 }
114 }
112 });
115 });
113
116
114 // repo list
117 // repo list
115 $('#repo_list_table').DataTable({
118 $('#repo_list_table').DataTable({
116 data: ${c.repos_data|n},
119 data: ${c.repos_data|n},
117 dom: 'rtp',
120 dom: 'rtp',
118 order: [[ 0, "asc" ]],
121 order: [[ 0, "asc" ]],
119 pageLength: ${c.visual.dashboard_items},
122 pageLength: ${c.visual.dashboard_items},
120 columns: [
123 columns: [
121 { data: {"_": "name",
124 { data: {"_": "name",
122 "sort": "name_raw"}, title: "${_('Name')}", className: "truncate-wrap td-componentname" },
125 "sort": "name_raw"}, title: "${_('Name')}", className: "truncate-wrap td-componentname" },
123 { data: 'menu', "bSortable": false, className: "quick_repo_menu" },
126 { data: 'menu', "bSortable": false, className: "quick_repo_menu" },
124 { data: {"_": "desc",
127 { data: {"_": "desc",
125 "sort": "desc"}, title: "${_('Description')}", className: "td-description" },
128 "sort": "desc"}, title: "${_('Description')}", className: "td-description" },
126 { data: {"_": "last_change",
129 { data: {"_": "last_change",
127 "sort": "last_change_raw",
130 "sort": "last_change_raw",
128 "type": Number}, title: "${_('Last Change')}", className: "td-time" },
131 "type": Number}, title: "${_('Last Change')}", className: "td-time" },
129 { data: {"_": "last_changeset",
132 { data: {"_": "last_changeset",
130 "sort": "last_changeset_raw",
133 "sort": "last_changeset_raw",
131 "type": Number}, title: "${_('Commit')}", className: "td-hash" },
134 "type": Number}, title: "${_('Commit')}", className: "td-hash" },
132 { data: {"_": "owner",
135 { data: {"_": "owner",
133 "sort": "owner"}, title: "${_('Owner')}", className: "td-user" },
136 "sort": "owner"}, title: "${_('Owner')}", className: "td-user" },
134 ],
137 ],
135 language: {
138 language: {
136 paginate: DEFAULT_GRID_PAGINATION,
139 paginate: DEFAULT_GRID_PAGINATION,
137 emptyTable: _gettext("No repositories available yet.")
140 emptyTable: _gettext("No repositories available yet.")
138 },
141 },
139 "drawCallback": function( settings, json ) {
142 "drawCallback": function( settings, json ) {
140 timeagoActivate();
143 timeagoActivate();
141 quick_repo_menu();
144 quick_repo_menu();
142 }
145 }
143 });
146 });
144
147
145 // update the counter when doing search
148 // update the counter when doing search
146 $('#repo_list_table, #group_list_table').on( 'search.dt', function (e,settings) {
149 $('#repo_list_table, #group_list_table').on( 'search.dt', function (e,settings) {
147 get_datatable_count();
150 get_datatable_count();
148 });
151 });
149
152
150 // filter, filter both grids
153 // filter, filter both grids
151 $('#q_filter').on( 'keyup', function () {
154 $('#q_filter').on( 'keyup', function () {
152 var repo_api = $('#repo_list_table').dataTable().api();
155 var repo_api = $('#repo_list_table').dataTable().api();
153 repo_api
156 repo_api
154 .columns( 0 )
157 .columns( 0 )
155 .search( this.value )
158 .search( this.value )
156 .draw();
159 .draw();
157
160
158 var repo_group_api = $('#group_list_table').dataTable().api();
161 var repo_group_api = $('#group_list_table').dataTable().api();
159 repo_group_api
162 repo_group_api
160 .columns( 0 )
163 .columns( 0 )
161 .search( this.value )
164 .search( this.value )
162 .draw();
165 .draw();
163 });
166 });
164
167
165 // refilter table if page load via back button
168 // refilter table if page load via back button
166 $("#q_filter").trigger('keyup');
169 $("#q_filter").trigger('keyup');
167
170
168 });
171 });
169 </script>
172 </script>
170 </%def>
173 </%def>
General Comments 0
You need to be logged in to leave comments. Login now