##// END OF EJS Templates
ux: better looking chat avatars
lisaq -
r836:ae0d550a default
parent child Browse files
Show More
@@ -1,220 +1,220 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2016 RhodeCode GmbH
3 # Copyright (C) 2016-2016 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 import logging
21 import logging
22 import os
22 import os
23
23
24 import itsdangerous
24 import itsdangerous
25 import requests
25 import requests
26
26
27 from dogpile.core import ReadWriteMutex
27 from dogpile.core import ReadWriteMutex
28
28
29 import rhodecode.lib.helpers as h
29 import rhodecode.lib.helpers as h
30
30
31 from rhodecode.lib.auth import HasRepoPermissionAny
31 from rhodecode.lib.auth import HasRepoPermissionAny
32 from rhodecode.lib.ext_json import json
32 from rhodecode.lib.ext_json import json
33 from rhodecode.model.db import User
33 from rhodecode.model.db import User
34
34
35 log = logging.getLogger(__name__)
35 log = logging.getLogger(__name__)
36
36
37 LOCK = ReadWriteMutex()
37 LOCK = ReadWriteMutex()
38
38
39 STATE_PUBLIC_KEYS = ['id', 'username', 'first_name', 'last_name',
39 STATE_PUBLIC_KEYS = ['id', 'username', 'first_name', 'last_name',
40 'icon_link', 'display_name', 'display_link']
40 'icon_link', 'display_name', 'display_link']
41
41
42
42
43 class ChannelstreamException(Exception):
43 class ChannelstreamException(Exception):
44 pass
44 pass
45
45
46
46
47 class ChannelstreamConnectionException(Exception):
47 class ChannelstreamConnectionException(Exception):
48 pass
48 pass
49
49
50
50
51 class ChannelstreamPermissionException(Exception):
51 class ChannelstreamPermissionException(Exception):
52 pass
52 pass
53
53
54
54
55 def channelstream_request(config, payload, endpoint, raise_exc=True):
55 def channelstream_request(config, payload, endpoint, raise_exc=True):
56 signer = itsdangerous.TimestampSigner(config['secret'])
56 signer = itsdangerous.TimestampSigner(config['secret'])
57 sig_for_server = signer.sign(endpoint)
57 sig_for_server = signer.sign(endpoint)
58 secret_headers = {'x-channelstream-secret': sig_for_server,
58 secret_headers = {'x-channelstream-secret': sig_for_server,
59 'x-channelstream-endpoint': endpoint,
59 'x-channelstream-endpoint': endpoint,
60 'Content-Type': 'application/json'}
60 'Content-Type': 'application/json'}
61 req_url = 'http://{}{}'.format(config['server'], endpoint)
61 req_url = 'http://{}{}'.format(config['server'], endpoint)
62 response = None
62 response = None
63 try:
63 try:
64 response = requests.post(req_url, data=json.dumps(payload),
64 response = requests.post(req_url, data=json.dumps(payload),
65 headers=secret_headers).json()
65 headers=secret_headers).json()
66 except requests.ConnectionError:
66 except requests.ConnectionError:
67 log.exception('ConnectionError happened')
67 log.exception('ConnectionError happened')
68 if raise_exc:
68 if raise_exc:
69 raise ChannelstreamConnectionException()
69 raise ChannelstreamConnectionException()
70 except Exception:
70 except Exception:
71 log.exception('Exception related to channelstream happened')
71 log.exception('Exception related to channelstream happened')
72 if raise_exc:
72 if raise_exc:
73 raise ChannelstreamConnectionException()
73 raise ChannelstreamConnectionException()
74 return response
74 return response
75
75
76
76
77 def get_user_data(user_id):
77 def get_user_data(user_id):
78 user = User.get(user_id)
78 user = User.get(user_id)
79 return {
79 return {
80 'id': user.user_id,
80 'id': user.user_id,
81 'username': user.username,
81 'username': user.username,
82 'first_name': user.name,
82 'first_name': user.name,
83 'last_name': user.lastname,
83 'last_name': user.lastname,
84 'icon_link': h.gravatar_url(user.email, 14),
84 'icon_link': h.gravatar_url(user.email, 60),
85 'display_name': h.person(user, 'username_or_name_or_email'),
85 'display_name': h.person(user, 'username_or_name_or_email'),
86 'display_link': h.link_to_user(user),
86 'display_link': h.link_to_user(user),
87 'notifications': user.user_data.get('notification_status', True)
87 'notifications': user.user_data.get('notification_status', True)
88 }
88 }
89
89
90
90
91 def broadcast_validator(channel_name):
91 def broadcast_validator(channel_name):
92 """ checks if user can access the broadcast channel """
92 """ checks if user can access the broadcast channel """
93 if channel_name == 'broadcast':
93 if channel_name == 'broadcast':
94 return True
94 return True
95
95
96
96
97 def repo_validator(channel_name):
97 def repo_validator(channel_name):
98 """ checks if user can access the broadcast channel """
98 """ checks if user can access the broadcast channel """
99 channel_prefix = '/repo$'
99 channel_prefix = '/repo$'
100 if channel_name.startswith(channel_prefix):
100 if channel_name.startswith(channel_prefix):
101 elements = channel_name[len(channel_prefix):].split('$')
101 elements = channel_name[len(channel_prefix):].split('$')
102 repo_name = elements[0]
102 repo_name = elements[0]
103 can_access = HasRepoPermissionAny(
103 can_access = HasRepoPermissionAny(
104 'repository.read',
104 'repository.read',
105 'repository.write',
105 'repository.write',
106 'repository.admin')(repo_name)
106 'repository.admin')(repo_name)
107 log.debug('permission check for {} channel '
107 log.debug('permission check for {} channel '
108 'resulted in {}'.format(repo_name, can_access))
108 'resulted in {}'.format(repo_name, can_access))
109 if can_access:
109 if can_access:
110 return True
110 return True
111 return False
111 return False
112
112
113
113
114 def check_channel_permissions(channels, plugin_validators, should_raise=True):
114 def check_channel_permissions(channels, plugin_validators, should_raise=True):
115 valid_channels = []
115 valid_channels = []
116
116
117 validators = [broadcast_validator, repo_validator]
117 validators = [broadcast_validator, repo_validator]
118 if plugin_validators:
118 if plugin_validators:
119 validators.extend(plugin_validators)
119 validators.extend(plugin_validators)
120 for channel_name in channels:
120 for channel_name in channels:
121 is_valid = False
121 is_valid = False
122 for validator in validators:
122 for validator in validators:
123 if validator(channel_name):
123 if validator(channel_name):
124 is_valid = True
124 is_valid = True
125 break
125 break
126 if is_valid:
126 if is_valid:
127 valid_channels.append(channel_name)
127 valid_channels.append(channel_name)
128 else:
128 else:
129 if should_raise:
129 if should_raise:
130 raise ChannelstreamPermissionException()
130 raise ChannelstreamPermissionException()
131 return valid_channels
131 return valid_channels
132
132
133
133
134 def get_channels_info(self, channels):
134 def get_channels_info(self, channels):
135 payload = {'channels': channels}
135 payload = {'channels': channels}
136 # gather persistence info
136 # gather persistence info
137 return channelstream_request(self._config(), payload, '/info')
137 return channelstream_request(self._config(), payload, '/info')
138
138
139
139
140 def parse_channels_info(info_result, include_channel_info=None):
140 def parse_channels_info(info_result, include_channel_info=None):
141 """
141 """
142 Returns data that contains only secure information that can be
142 Returns data that contains only secure information that can be
143 presented to clients
143 presented to clients
144 """
144 """
145 include_channel_info = include_channel_info or []
145 include_channel_info = include_channel_info or []
146
146
147 user_state_dict = {}
147 user_state_dict = {}
148 for userinfo in info_result['users']:
148 for userinfo in info_result['users']:
149 user_state_dict[userinfo['user']] = {
149 user_state_dict[userinfo['user']] = {
150 k: v for k, v in userinfo['state'].items()
150 k: v for k, v in userinfo['state'].items()
151 if k in STATE_PUBLIC_KEYS
151 if k in STATE_PUBLIC_KEYS
152 }
152 }
153
153
154 channels_info = {}
154 channels_info = {}
155
155
156 for c_name, c_info in info_result['channels'].items():
156 for c_name, c_info in info_result['channels'].items():
157 if c_name not in include_channel_info:
157 if c_name not in include_channel_info:
158 continue
158 continue
159 connected_list = []
159 connected_list = []
160 for userinfo in c_info['users']:
160 for userinfo in c_info['users']:
161 connected_list.append({
161 connected_list.append({
162 'user': userinfo['user'],
162 'user': userinfo['user'],
163 'state': user_state_dict[userinfo['user']]
163 'state': user_state_dict[userinfo['user']]
164 })
164 })
165 channels_info[c_name] = {'users': connected_list,
165 channels_info[c_name] = {'users': connected_list,
166 'history': c_info['history']}
166 'history': c_info['history']}
167
167
168 return channels_info
168 return channels_info
169
169
170
170
171 def log_filepath(history_location, channel_name):
171 def log_filepath(history_location, channel_name):
172 filename = '{}.log'.format(channel_name.encode('hex'))
172 filename = '{}.log'.format(channel_name.encode('hex'))
173 filepath = os.path.join(history_location, filename)
173 filepath = os.path.join(history_location, filename)
174 return filepath
174 return filepath
175
175
176
176
177 def read_history(history_location, channel_name):
177 def read_history(history_location, channel_name):
178 filepath = log_filepath(history_location, channel_name)
178 filepath = log_filepath(history_location, channel_name)
179 if not os.path.exists(filepath):
179 if not os.path.exists(filepath):
180 return []
180 return []
181 history_lines_limit = -100
181 history_lines_limit = -100
182 history = []
182 history = []
183 with open(filepath, 'rb') as f:
183 with open(filepath, 'rb') as f:
184 for line in f.readlines()[history_lines_limit:]:
184 for line in f.readlines()[history_lines_limit:]:
185 try:
185 try:
186 history.append(json.loads(line))
186 history.append(json.loads(line))
187 except Exception:
187 except Exception:
188 log.exception('Failed to load history')
188 log.exception('Failed to load history')
189 return history
189 return history
190
190
191
191
192 def update_history_from_logs(config, channels, payload):
192 def update_history_from_logs(config, channels, payload):
193 history_location = config.get('history.location')
193 history_location = config.get('history.location')
194 for channel in channels:
194 for channel in channels:
195 history = read_history(history_location, channel)
195 history = read_history(history_location, channel)
196 payload['channels_info'][channel]['history'] = history
196 payload['channels_info'][channel]['history'] = history
197
197
198
198
199 def write_history(config, message):
199 def write_history(config, message):
200 """ writes a messge to a base64encoded filename """
200 """ writes a messge to a base64encoded filename """
201 history_location = config.get('history.location')
201 history_location = config.get('history.location')
202 if not os.path.exists(history_location):
202 if not os.path.exists(history_location):
203 return
203 return
204 try:
204 try:
205 LOCK.acquire_write_lock()
205 LOCK.acquire_write_lock()
206 filepath = log_filepath(history_location, message['channel'])
206 filepath = log_filepath(history_location, message['channel'])
207 with open(filepath, 'ab') as f:
207 with open(filepath, 'ab') as f:
208 json.dump(message, f)
208 json.dump(message, f)
209 f.write('\n')
209 f.write('\n')
210 finally:
210 finally:
211 LOCK.release_write_lock()
211 LOCK.release_write_lock()
212
212
213
213
214 def get_connection_validators(registry):
214 def get_connection_validators(registry):
215 validators = []
215 validators = []
216 for k, config in registry.rhodecode_plugins.iteritems():
216 for k, config in registry.rhodecode_plugins.iteritems():
217 validator = config.get('channelstream', {}).get('connect_validator')
217 validator = config.get('channelstream', {}).get('connect_validator')
218 if validator:
218 if validator:
219 validators.append(validator)
219 validators.append(validator)
220 return validators
220 return validators
@@ -1,1053 +1,1053 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 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
31 from datetime import datetime
32
32
33 from sqlalchemy.sql import func
33 from sqlalchemy.sql import func
34 from sqlalchemy.sql.expression import true, or_
34 from sqlalchemy.sql.expression import true, or_
35 from zope.cachedescriptors.property import Lazy as LazyProperty
35 from zope.cachedescriptors.property import Lazy as LazyProperty
36
36
37 from rhodecode import events
37 from rhodecode import events
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import HasUserGroupPermissionAny
39 from rhodecode.lib.auth import HasUserGroupPermissionAny
40 from rhodecode.lib.caching_query import FromCache
40 from rhodecode.lib.caching_query import FromCache
41 from rhodecode.lib.exceptions import AttachedForksError
41 from rhodecode.lib.exceptions import AttachedForksError
42 from rhodecode.lib.hooks_base import log_delete_repository
42 from rhodecode.lib.hooks_base import log_delete_repository
43 from rhodecode.lib.markup_renderer import MarkupRenderer
43 from rhodecode.lib.markup_renderer import MarkupRenderer
44 from rhodecode.lib.utils import make_db_config
44 from rhodecode.lib.utils import make_db_config
45 from rhodecode.lib.utils2 import (
45 from rhodecode.lib.utils2 import (
46 safe_str, safe_unicode, remove_prefix, obfuscate_url_pw,
46 safe_str, safe_unicode, remove_prefix, obfuscate_url_pw,
47 get_current_rhodecode_user, safe_int, datetime_to_time, action_logger_generic)
47 get_current_rhodecode_user, safe_int, datetime_to_time, action_logger_generic)
48 from rhodecode.lib.vcs.backends import get_backend
48 from rhodecode.lib.vcs.backends import get_backend
49 from rhodecode.lib.vcs.exceptions import NodeDoesNotExistError
49 from rhodecode.lib.vcs.exceptions import NodeDoesNotExistError
50 from rhodecode.model import BaseModel
50 from rhodecode.model import BaseModel
51 from rhodecode.model.db import (
51 from rhodecode.model.db import (
52 Repository, UserRepoToPerm, UserGroupRepoToPerm, UserRepoGroupToPerm,
52 Repository, UserRepoToPerm, UserGroupRepoToPerm, UserRepoGroupToPerm,
53 UserGroupRepoGroupToPerm, User, Permission, Statistics, UserGroup,
53 UserGroupRepoGroupToPerm, User, Permission, Statistics, UserGroup,
54 RepoGroup, RepositoryField)
54 RepoGroup, RepositoryField)
55 from rhodecode.model.scm import UserGroupList
55 from rhodecode.model.scm import UserGroupList
56 from rhodecode.model.settings import VcsSettingsModel
56 from rhodecode.model.settings import VcsSettingsModel
57
57
58
58
59 log = logging.getLogger(__name__)
59 log = logging.getLogger(__name__)
60
60
61
61
62 class RepoModel(BaseModel):
62 class RepoModel(BaseModel):
63
63
64 cls = Repository
64 cls = Repository
65
65
66 def _get_user_group(self, users_group):
66 def _get_user_group(self, users_group):
67 return self._get_instance(UserGroup, users_group,
67 return self._get_instance(UserGroup, users_group,
68 callback=UserGroup.get_by_group_name)
68 callback=UserGroup.get_by_group_name)
69
69
70 def _get_repo_group(self, repo_group):
70 def _get_repo_group(self, repo_group):
71 return self._get_instance(RepoGroup, repo_group,
71 return self._get_instance(RepoGroup, repo_group,
72 callback=RepoGroup.get_by_group_name)
72 callback=RepoGroup.get_by_group_name)
73
73
74 def _create_default_perms(self, repository, private):
74 def _create_default_perms(self, repository, private):
75 # create default permission
75 # create default permission
76 default = 'repository.read'
76 default = 'repository.read'
77 def_user = User.get_default_user()
77 def_user = User.get_default_user()
78 for p in def_user.user_perms:
78 for p in def_user.user_perms:
79 if p.permission.permission_name.startswith('repository.'):
79 if p.permission.permission_name.startswith('repository.'):
80 default = p.permission.permission_name
80 default = p.permission.permission_name
81 break
81 break
82
82
83 default_perm = 'repository.none' if private else default
83 default_perm = 'repository.none' if private else default
84
84
85 repo_to_perm = UserRepoToPerm()
85 repo_to_perm = UserRepoToPerm()
86 repo_to_perm.permission = Permission.get_by_key(default_perm)
86 repo_to_perm.permission = Permission.get_by_key(default_perm)
87
87
88 repo_to_perm.repository = repository
88 repo_to_perm.repository = repository
89 repo_to_perm.user_id = def_user.user_id
89 repo_to_perm.user_id = def_user.user_id
90
90
91 return repo_to_perm
91 return repo_to_perm
92
92
93 @LazyProperty
93 @LazyProperty
94 def repos_path(self):
94 def repos_path(self):
95 """
95 """
96 Gets the repositories root path from database
96 Gets the repositories root path from database
97 """
97 """
98 settings_model = VcsSettingsModel(sa=self.sa)
98 settings_model = VcsSettingsModel(sa=self.sa)
99 return settings_model.get_repos_location()
99 return settings_model.get_repos_location()
100
100
101 def get(self, repo_id, cache=False):
101 def get(self, repo_id, cache=False):
102 repo = self.sa.query(Repository) \
102 repo = self.sa.query(Repository) \
103 .filter(Repository.repo_id == repo_id)
103 .filter(Repository.repo_id == repo_id)
104
104
105 if cache:
105 if cache:
106 repo = repo.options(FromCache("sql_cache_short",
106 repo = repo.options(FromCache("sql_cache_short",
107 "get_repo_%s" % repo_id))
107 "get_repo_%s" % repo_id))
108 return repo.scalar()
108 return repo.scalar()
109
109
110 def get_repo(self, repository):
110 def get_repo(self, repository):
111 return self._get_repo(repository)
111 return self._get_repo(repository)
112
112
113 def get_by_repo_name(self, repo_name, cache=False):
113 def get_by_repo_name(self, repo_name, cache=False):
114 repo = self.sa.query(Repository) \
114 repo = self.sa.query(Repository) \
115 .filter(Repository.repo_name == repo_name)
115 .filter(Repository.repo_name == repo_name)
116
116
117 if cache:
117 if cache:
118 repo = repo.options(FromCache("sql_cache_short",
118 repo = repo.options(FromCache("sql_cache_short",
119 "get_repo_%s" % repo_name))
119 "get_repo_%s" % repo_name))
120 return repo.scalar()
120 return repo.scalar()
121
121
122 def _extract_id_from_repo_name(self, repo_name):
122 def _extract_id_from_repo_name(self, repo_name):
123 if repo_name.startswith('/'):
123 if repo_name.startswith('/'):
124 repo_name = repo_name.lstrip('/')
124 repo_name = repo_name.lstrip('/')
125 by_id_match = re.match(r'^_(\d{1,})', repo_name)
125 by_id_match = re.match(r'^_(\d{1,})', repo_name)
126 if by_id_match:
126 if by_id_match:
127 return by_id_match.groups()[0]
127 return by_id_match.groups()[0]
128
128
129 def get_repo_by_id(self, repo_name):
129 def get_repo_by_id(self, repo_name):
130 """
130 """
131 Extracts repo_name by id from special urls.
131 Extracts repo_name by id from special urls.
132 Example url is _11/repo_name
132 Example url is _11/repo_name
133
133
134 :param repo_name:
134 :param repo_name:
135 :return: repo object if matched else None
135 :return: repo object if matched else None
136 """
136 """
137 try:
137 try:
138 _repo_id = self._extract_id_from_repo_name(repo_name)
138 _repo_id = self._extract_id_from_repo_name(repo_name)
139 if _repo_id:
139 if _repo_id:
140 return self.get(_repo_id)
140 return self.get(_repo_id)
141 except Exception:
141 except Exception:
142 log.exception('Failed to extract repo_name from URL')
142 log.exception('Failed to extract repo_name from URL')
143
143
144 return None
144 return None
145
145
146 def get_url(self, repo):
146 def get_url(self, repo):
147 return h.url('summary_home', repo_name=safe_str(repo.repo_name),
147 return h.url('summary_home', repo_name=safe_str(repo.repo_name),
148 qualified=True)
148 qualified=True)
149
149
150 def get_users(self, name_contains=None, limit=20, only_active=True):
150 def get_users(self, name_contains=None, limit=20, only_active=True):
151 # TODO: mikhail: move this method to the UserModel.
151 # TODO: mikhail: move this method to the UserModel.
152 query = self.sa.query(User)
152 query = self.sa.query(User)
153 if only_active:
153 if only_active:
154 query = query.filter(User.active == true())
154 query = query.filter(User.active == true())
155
155
156 if name_contains:
156 if name_contains:
157 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
157 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
158 query = query.filter(
158 query = query.filter(
159 or_(
159 or_(
160 User.name.ilike(ilike_expression),
160 User.name.ilike(ilike_expression),
161 User.lastname.ilike(ilike_expression),
161 User.lastname.ilike(ilike_expression),
162 User.username.ilike(ilike_expression)
162 User.username.ilike(ilike_expression)
163 )
163 )
164 )
164 )
165 query = query.limit(limit)
165 query = query.limit(limit)
166 users = query.all()
166 users = query.all()
167
167
168 _users = [
168 _users = [
169 {
169 {
170 'id': user.user_id,
170 'id': user.user_id,
171 'first_name': user.name,
171 'first_name': user.name,
172 'last_name': user.lastname,
172 'last_name': user.lastname,
173 'username': user.username,
173 'username': user.username,
174 'icon_link': h.gravatar_url(user.email, 14),
174 'icon_link': h.gravatar_url(user.email, 30),
175 'value_display': h.person(user.email),
175 'value_display': h.person(user.email),
176 'value': user.username,
176 'value': user.username,
177 'value_type': 'user',
177 'value_type': 'user',
178 'active': user.active,
178 'active': user.active,
179 }
179 }
180 for user in users
180 for user in users
181 ]
181 ]
182 return _users
182 return _users
183
183
184 def get_user_groups(self, name_contains=None, limit=20, only_active=True):
184 def get_user_groups(self, name_contains=None, limit=20, only_active=True):
185 # TODO: mikhail: move this method to the UserGroupModel.
185 # TODO: mikhail: move this method to the UserGroupModel.
186 query = self.sa.query(UserGroup)
186 query = self.sa.query(UserGroup)
187 if only_active:
187 if only_active:
188 query = query.filter(UserGroup.users_group_active == true())
188 query = query.filter(UserGroup.users_group_active == true())
189
189
190 if name_contains:
190 if name_contains:
191 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
191 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
192 query = query.filter(
192 query = query.filter(
193 UserGroup.users_group_name.ilike(ilike_expression))\
193 UserGroup.users_group_name.ilike(ilike_expression))\
194 .order_by(func.length(UserGroup.users_group_name))\
194 .order_by(func.length(UserGroup.users_group_name))\
195 .order_by(UserGroup.users_group_name)
195 .order_by(UserGroup.users_group_name)
196
196
197 query = query.limit(limit)
197 query = query.limit(limit)
198 user_groups = query.all()
198 user_groups = query.all()
199 perm_set = ['usergroup.read', 'usergroup.write', 'usergroup.admin']
199 perm_set = ['usergroup.read', 'usergroup.write', 'usergroup.admin']
200 user_groups = UserGroupList(user_groups, perm_set=perm_set)
200 user_groups = UserGroupList(user_groups, perm_set=perm_set)
201
201
202 _groups = [
202 _groups = [
203 {
203 {
204 'id': group.users_group_id,
204 'id': group.users_group_id,
205 # TODO: marcink figure out a way to generate the url for the
205 # TODO: marcink figure out a way to generate the url for the
206 # icon
206 # icon
207 'icon_link': '',
207 'icon_link': '',
208 'value_display': 'Group: %s (%d members)' % (
208 'value_display': 'Group: %s (%d members)' % (
209 group.users_group_name, len(group.members),),
209 group.users_group_name, len(group.members),),
210 'value': group.users_group_name,
210 'value': group.users_group_name,
211 'value_type': 'user_group',
211 'value_type': 'user_group',
212 'active': group.users_group_active,
212 'active': group.users_group_active,
213 }
213 }
214 for group in user_groups
214 for group in user_groups
215 ]
215 ]
216 return _groups
216 return _groups
217
217
218 @classmethod
218 @classmethod
219 def update_repoinfo(cls, repositories=None):
219 def update_repoinfo(cls, repositories=None):
220 if not repositories:
220 if not repositories:
221 repositories = Repository.getAll()
221 repositories = Repository.getAll()
222 for repo in repositories:
222 for repo in repositories:
223 repo.update_commit_cache()
223 repo.update_commit_cache()
224
224
225 def get_repos_as_dict(self, repo_list=None, admin=False,
225 def get_repos_as_dict(self, repo_list=None, admin=False,
226 super_user_actions=False):
226 super_user_actions=False):
227
227
228 from rhodecode.lib.utils import PartialRenderer
228 from rhodecode.lib.utils import PartialRenderer
229 _render = PartialRenderer('data_table/_dt_elements.html')
229 _render = PartialRenderer('data_table/_dt_elements.html')
230 c = _render.c
230 c = _render.c
231
231
232 def quick_menu(repo_name):
232 def quick_menu(repo_name):
233 return _render('quick_menu', repo_name)
233 return _render('quick_menu', repo_name)
234
234
235 def repo_lnk(name, rtype, rstate, private, fork_of):
235 def repo_lnk(name, rtype, rstate, private, fork_of):
236 return _render('repo_name', name, rtype, rstate, private, fork_of,
236 return _render('repo_name', name, rtype, rstate, private, fork_of,
237 short_name=not admin, admin=False)
237 short_name=not admin, admin=False)
238
238
239 def last_change(last_change):
239 def last_change(last_change):
240 return _render("last_change", last_change)
240 return _render("last_change", last_change)
241
241
242 def rss_lnk(repo_name):
242 def rss_lnk(repo_name):
243 return _render("rss", repo_name)
243 return _render("rss", repo_name)
244
244
245 def atom_lnk(repo_name):
245 def atom_lnk(repo_name):
246 return _render("atom", repo_name)
246 return _render("atom", repo_name)
247
247
248 def last_rev(repo_name, cs_cache):
248 def last_rev(repo_name, cs_cache):
249 return _render('revision', repo_name, cs_cache.get('revision'),
249 return _render('revision', repo_name, cs_cache.get('revision'),
250 cs_cache.get('raw_id'), cs_cache.get('author'),
250 cs_cache.get('raw_id'), cs_cache.get('author'),
251 cs_cache.get('message'))
251 cs_cache.get('message'))
252
252
253 def desc(desc):
253 def desc(desc):
254 if c.visual.stylify_metatags:
254 if c.visual.stylify_metatags:
255 return h.urlify_text(h.escaped_stylize(h.truncate(desc, 60)))
255 return h.urlify_text(h.escaped_stylize(h.truncate(desc, 60)))
256 else:
256 else:
257 return h.urlify_text(h.html_escape(h.truncate(desc, 60)))
257 return h.urlify_text(h.html_escape(h.truncate(desc, 60)))
258
258
259 def state(repo_state):
259 def state(repo_state):
260 return _render("repo_state", repo_state)
260 return _render("repo_state", repo_state)
261
261
262 def repo_actions(repo_name):
262 def repo_actions(repo_name):
263 return _render('repo_actions', repo_name, super_user_actions)
263 return _render('repo_actions', repo_name, super_user_actions)
264
264
265 def user_profile(username):
265 def user_profile(username):
266 return _render('user_profile', username)
266 return _render('user_profile', username)
267
267
268 repos_data = []
268 repos_data = []
269 for repo in repo_list:
269 for repo in repo_list:
270 cs_cache = repo.changeset_cache
270 cs_cache = repo.changeset_cache
271 row = {
271 row = {
272 "menu": quick_menu(repo.repo_name),
272 "menu": quick_menu(repo.repo_name),
273
273
274 "name": repo_lnk(repo.repo_name, repo.repo_type,
274 "name": repo_lnk(repo.repo_name, repo.repo_type,
275 repo.repo_state, repo.private, repo.fork),
275 repo.repo_state, repo.private, repo.fork),
276 "name_raw": repo.repo_name.lower(),
276 "name_raw": repo.repo_name.lower(),
277
277
278 "last_change": last_change(repo.last_db_change),
278 "last_change": last_change(repo.last_db_change),
279 "last_change_raw": datetime_to_time(repo.last_db_change),
279 "last_change_raw": datetime_to_time(repo.last_db_change),
280
280
281 "last_changeset": last_rev(repo.repo_name, cs_cache),
281 "last_changeset": last_rev(repo.repo_name, cs_cache),
282 "last_changeset_raw": cs_cache.get('revision'),
282 "last_changeset_raw": cs_cache.get('revision'),
283
283
284 "desc": desc(repo.description),
284 "desc": desc(repo.description),
285 "owner": user_profile(repo.user.username),
285 "owner": user_profile(repo.user.username),
286
286
287 "state": state(repo.repo_state),
287 "state": state(repo.repo_state),
288 "rss": rss_lnk(repo.repo_name),
288 "rss": rss_lnk(repo.repo_name),
289
289
290 "atom": atom_lnk(repo.repo_name),
290 "atom": atom_lnk(repo.repo_name),
291 }
291 }
292 if admin:
292 if admin:
293 row.update({
293 row.update({
294 "action": repo_actions(repo.repo_name),
294 "action": repo_actions(repo.repo_name),
295 })
295 })
296 repos_data.append(row)
296 repos_data.append(row)
297
297
298 return repos_data
298 return repos_data
299
299
300 def _get_defaults(self, repo_name):
300 def _get_defaults(self, repo_name):
301 """
301 """
302 Gets information about repository, and returns a dict for
302 Gets information about repository, and returns a dict for
303 usage in forms
303 usage in forms
304
304
305 :param repo_name:
305 :param repo_name:
306 """
306 """
307
307
308 repo_info = Repository.get_by_repo_name(repo_name)
308 repo_info = Repository.get_by_repo_name(repo_name)
309
309
310 if repo_info is None:
310 if repo_info is None:
311 return None
311 return None
312
312
313 defaults = repo_info.get_dict()
313 defaults = repo_info.get_dict()
314 defaults['repo_name'] = repo_info.just_name
314 defaults['repo_name'] = repo_info.just_name
315
315
316 groups = repo_info.groups_with_parents
316 groups = repo_info.groups_with_parents
317 parent_group = groups[-1] if groups else None
317 parent_group = groups[-1] if groups else None
318
318
319 # we use -1 as this is how in HTML, we mark an empty group
319 # we use -1 as this is how in HTML, we mark an empty group
320 defaults['repo_group'] = getattr(parent_group, 'group_id', -1)
320 defaults['repo_group'] = getattr(parent_group, 'group_id', -1)
321
321
322 keys_to_process = (
322 keys_to_process = (
323 {'k': 'repo_type', 'strip': False},
323 {'k': 'repo_type', 'strip': False},
324 {'k': 'repo_enable_downloads', 'strip': True},
324 {'k': 'repo_enable_downloads', 'strip': True},
325 {'k': 'repo_description', 'strip': True},
325 {'k': 'repo_description', 'strip': True},
326 {'k': 'repo_enable_locking', 'strip': True},
326 {'k': 'repo_enable_locking', 'strip': True},
327 {'k': 'repo_landing_rev', 'strip': True},
327 {'k': 'repo_landing_rev', 'strip': True},
328 {'k': 'clone_uri', 'strip': False},
328 {'k': 'clone_uri', 'strip': False},
329 {'k': 'repo_private', 'strip': True},
329 {'k': 'repo_private', 'strip': True},
330 {'k': 'repo_enable_statistics', 'strip': True}
330 {'k': 'repo_enable_statistics', 'strip': True}
331 )
331 )
332
332
333 for item in keys_to_process:
333 for item in keys_to_process:
334 attr = item['k']
334 attr = item['k']
335 if item['strip']:
335 if item['strip']:
336 attr = remove_prefix(item['k'], 'repo_')
336 attr = remove_prefix(item['k'], 'repo_')
337
337
338 val = defaults[attr]
338 val = defaults[attr]
339 if item['k'] == 'repo_landing_rev':
339 if item['k'] == 'repo_landing_rev':
340 val = ':'.join(defaults[attr])
340 val = ':'.join(defaults[attr])
341 defaults[item['k']] = val
341 defaults[item['k']] = val
342 if item['k'] == 'clone_uri':
342 if item['k'] == 'clone_uri':
343 defaults['clone_uri_hidden'] = repo_info.clone_uri_hidden
343 defaults['clone_uri_hidden'] = repo_info.clone_uri_hidden
344
344
345 # fill owner
345 # fill owner
346 if repo_info.user:
346 if repo_info.user:
347 defaults.update({'user': repo_info.user.username})
347 defaults.update({'user': repo_info.user.username})
348 else:
348 else:
349 replacement_user = User.get_first_super_admin().username
349 replacement_user = User.get_first_super_admin().username
350 defaults.update({'user': replacement_user})
350 defaults.update({'user': replacement_user})
351
351
352 # fill repository users
352 # fill repository users
353 for p in repo_info.repo_to_perm:
353 for p in repo_info.repo_to_perm:
354 defaults.update({'u_perm_%s' % p.user.user_id:
354 defaults.update({'u_perm_%s' % p.user.user_id:
355 p.permission.permission_name})
355 p.permission.permission_name})
356
356
357 # fill repository groups
357 # fill repository groups
358 for p in repo_info.users_group_to_perm:
358 for p in repo_info.users_group_to_perm:
359 defaults.update({'g_perm_%s' % p.users_group.users_group_id:
359 defaults.update({'g_perm_%s' % p.users_group.users_group_id:
360 p.permission.permission_name})
360 p.permission.permission_name})
361
361
362 return defaults
362 return defaults
363
363
364 def update(self, repo, **kwargs):
364 def update(self, repo, **kwargs):
365 try:
365 try:
366 cur_repo = self._get_repo(repo)
366 cur_repo = self._get_repo(repo)
367 source_repo_name = cur_repo.repo_name
367 source_repo_name = cur_repo.repo_name
368 if 'user' in kwargs:
368 if 'user' in kwargs:
369 cur_repo.user = User.get_by_username(kwargs['user'])
369 cur_repo.user = User.get_by_username(kwargs['user'])
370
370
371 if 'repo_group' in kwargs:
371 if 'repo_group' in kwargs:
372 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
372 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
373 log.debug('Updating repo %s with params:%s', cur_repo, kwargs)
373 log.debug('Updating repo %s with params:%s', cur_repo, kwargs)
374
374
375 update_keys = [
375 update_keys = [
376 (1, 'repo_enable_downloads'),
376 (1, 'repo_enable_downloads'),
377 (1, 'repo_description'),
377 (1, 'repo_description'),
378 (1, 'repo_enable_locking'),
378 (1, 'repo_enable_locking'),
379 (1, 'repo_landing_rev'),
379 (1, 'repo_landing_rev'),
380 (1, 'repo_private'),
380 (1, 'repo_private'),
381 (1, 'repo_enable_statistics'),
381 (1, 'repo_enable_statistics'),
382 (0, 'clone_uri'),
382 (0, 'clone_uri'),
383 (0, 'fork_id')
383 (0, 'fork_id')
384 ]
384 ]
385 for strip, k in update_keys:
385 for strip, k in update_keys:
386 if k in kwargs:
386 if k in kwargs:
387 val = kwargs[k]
387 val = kwargs[k]
388 if strip:
388 if strip:
389 k = remove_prefix(k, 'repo_')
389 k = remove_prefix(k, 'repo_')
390 if k == 'clone_uri':
390 if k == 'clone_uri':
391 from rhodecode.model.validators import Missing
391 from rhodecode.model.validators import Missing
392 _change = kwargs.get('clone_uri_change')
392 _change = kwargs.get('clone_uri_change')
393 if _change in [Missing, 'OLD']:
393 if _change in [Missing, 'OLD']:
394 # we don't change the value, so use original one
394 # we don't change the value, so use original one
395 val = cur_repo.clone_uri
395 val = cur_repo.clone_uri
396
396
397 setattr(cur_repo, k, val)
397 setattr(cur_repo, k, val)
398
398
399 new_name = cur_repo.get_new_name(kwargs['repo_name'])
399 new_name = cur_repo.get_new_name(kwargs['repo_name'])
400 cur_repo.repo_name = new_name
400 cur_repo.repo_name = new_name
401
401
402 # if private flag is set, reset default permission to NONE
402 # if private flag is set, reset default permission to NONE
403 if kwargs.get('repo_private'):
403 if kwargs.get('repo_private'):
404 EMPTY_PERM = 'repository.none'
404 EMPTY_PERM = 'repository.none'
405 RepoModel().grant_user_permission(
405 RepoModel().grant_user_permission(
406 repo=cur_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM
406 repo=cur_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM
407 )
407 )
408
408
409 # handle extra fields
409 # handle extra fields
410 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX),
410 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX),
411 kwargs):
411 kwargs):
412 k = RepositoryField.un_prefix_key(field)
412 k = RepositoryField.un_prefix_key(field)
413 ex_field = RepositoryField.get_by_key_name(
413 ex_field = RepositoryField.get_by_key_name(
414 key=k, repo=cur_repo)
414 key=k, repo=cur_repo)
415 if ex_field:
415 if ex_field:
416 ex_field.field_value = kwargs[field]
416 ex_field.field_value = kwargs[field]
417 self.sa.add(ex_field)
417 self.sa.add(ex_field)
418 self.sa.add(cur_repo)
418 self.sa.add(cur_repo)
419
419
420 if source_repo_name != new_name:
420 if source_repo_name != new_name:
421 # rename repository
421 # rename repository
422 self._rename_filesystem_repo(
422 self._rename_filesystem_repo(
423 old=source_repo_name, new=new_name)
423 old=source_repo_name, new=new_name)
424
424
425 return cur_repo
425 return cur_repo
426 except Exception:
426 except Exception:
427 log.error(traceback.format_exc())
427 log.error(traceback.format_exc())
428 raise
428 raise
429
429
430 def _create_repo(self, repo_name, repo_type, description, owner,
430 def _create_repo(self, repo_name, repo_type, description, owner,
431 private=False, clone_uri=None, repo_group=None,
431 private=False, clone_uri=None, repo_group=None,
432 landing_rev='rev:tip', fork_of=None,
432 landing_rev='rev:tip', fork_of=None,
433 copy_fork_permissions=False, enable_statistics=False,
433 copy_fork_permissions=False, enable_statistics=False,
434 enable_locking=False, enable_downloads=False,
434 enable_locking=False, enable_downloads=False,
435 copy_group_permissions=False,
435 copy_group_permissions=False,
436 state=Repository.STATE_PENDING):
436 state=Repository.STATE_PENDING):
437 """
437 """
438 Create repository inside database with PENDING state, this should be
438 Create repository inside database with PENDING state, this should be
439 only executed by create() repo. With exception of importing existing
439 only executed by create() repo. With exception of importing existing
440 repos
440 repos
441 """
441 """
442 from rhodecode.model.scm import ScmModel
442 from rhodecode.model.scm import ScmModel
443
443
444 owner = self._get_user(owner)
444 owner = self._get_user(owner)
445 fork_of = self._get_repo(fork_of)
445 fork_of = self._get_repo(fork_of)
446 repo_group = self._get_repo_group(safe_int(repo_group))
446 repo_group = self._get_repo_group(safe_int(repo_group))
447
447
448 try:
448 try:
449 repo_name = safe_unicode(repo_name)
449 repo_name = safe_unicode(repo_name)
450 description = safe_unicode(description)
450 description = safe_unicode(description)
451 # repo name is just a name of repository
451 # repo name is just a name of repository
452 # while repo_name_full is a full qualified name that is combined
452 # while repo_name_full is a full qualified name that is combined
453 # with name and path of group
453 # with name and path of group
454 repo_name_full = repo_name
454 repo_name_full = repo_name
455 repo_name = repo_name.split(Repository.NAME_SEP)[-1]
455 repo_name = repo_name.split(Repository.NAME_SEP)[-1]
456
456
457 new_repo = Repository()
457 new_repo = Repository()
458 new_repo.repo_state = state
458 new_repo.repo_state = state
459 new_repo.enable_statistics = False
459 new_repo.enable_statistics = False
460 new_repo.repo_name = repo_name_full
460 new_repo.repo_name = repo_name_full
461 new_repo.repo_type = repo_type
461 new_repo.repo_type = repo_type
462 new_repo.user = owner
462 new_repo.user = owner
463 new_repo.group = repo_group
463 new_repo.group = repo_group
464 new_repo.description = description or repo_name
464 new_repo.description = description or repo_name
465 new_repo.private = private
465 new_repo.private = private
466 new_repo.clone_uri = clone_uri
466 new_repo.clone_uri = clone_uri
467 new_repo.landing_rev = landing_rev
467 new_repo.landing_rev = landing_rev
468
468
469 new_repo.enable_statistics = enable_statistics
469 new_repo.enable_statistics = enable_statistics
470 new_repo.enable_locking = enable_locking
470 new_repo.enable_locking = enable_locking
471 new_repo.enable_downloads = enable_downloads
471 new_repo.enable_downloads = enable_downloads
472
472
473 if repo_group:
473 if repo_group:
474 new_repo.enable_locking = repo_group.enable_locking
474 new_repo.enable_locking = repo_group.enable_locking
475
475
476 if fork_of:
476 if fork_of:
477 parent_repo = fork_of
477 parent_repo = fork_of
478 new_repo.fork = parent_repo
478 new_repo.fork = parent_repo
479
479
480 events.trigger(events.RepoPreCreateEvent(new_repo))
480 events.trigger(events.RepoPreCreateEvent(new_repo))
481
481
482 self.sa.add(new_repo)
482 self.sa.add(new_repo)
483
483
484 EMPTY_PERM = 'repository.none'
484 EMPTY_PERM = 'repository.none'
485 if fork_of and copy_fork_permissions:
485 if fork_of and copy_fork_permissions:
486 repo = fork_of
486 repo = fork_of
487 user_perms = UserRepoToPerm.query() \
487 user_perms = UserRepoToPerm.query() \
488 .filter(UserRepoToPerm.repository == repo).all()
488 .filter(UserRepoToPerm.repository == repo).all()
489 group_perms = UserGroupRepoToPerm.query() \
489 group_perms = UserGroupRepoToPerm.query() \
490 .filter(UserGroupRepoToPerm.repository == repo).all()
490 .filter(UserGroupRepoToPerm.repository == repo).all()
491
491
492 for perm in user_perms:
492 for perm in user_perms:
493 UserRepoToPerm.create(
493 UserRepoToPerm.create(
494 perm.user, new_repo, perm.permission)
494 perm.user, new_repo, perm.permission)
495
495
496 for perm in group_perms:
496 for perm in group_perms:
497 UserGroupRepoToPerm.create(
497 UserGroupRepoToPerm.create(
498 perm.users_group, new_repo, perm.permission)
498 perm.users_group, new_repo, perm.permission)
499 # in case we copy permissions and also set this repo to private
499 # in case we copy permissions and also set this repo to private
500 # override the default user permission to make it a private
500 # override the default user permission to make it a private
501 # repo
501 # repo
502 if private:
502 if private:
503 RepoModel(self.sa).grant_user_permission(
503 RepoModel(self.sa).grant_user_permission(
504 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
504 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
505
505
506 elif repo_group and copy_group_permissions:
506 elif repo_group and copy_group_permissions:
507 user_perms = UserRepoGroupToPerm.query() \
507 user_perms = UserRepoGroupToPerm.query() \
508 .filter(UserRepoGroupToPerm.group == repo_group).all()
508 .filter(UserRepoGroupToPerm.group == repo_group).all()
509
509
510 group_perms = UserGroupRepoGroupToPerm.query() \
510 group_perms = UserGroupRepoGroupToPerm.query() \
511 .filter(UserGroupRepoGroupToPerm.group == repo_group).all()
511 .filter(UserGroupRepoGroupToPerm.group == repo_group).all()
512
512
513 for perm in user_perms:
513 for perm in user_perms:
514 perm_name = perm.permission.permission_name.replace(
514 perm_name = perm.permission.permission_name.replace(
515 'group.', 'repository.')
515 'group.', 'repository.')
516 perm_obj = Permission.get_by_key(perm_name)
516 perm_obj = Permission.get_by_key(perm_name)
517 UserRepoToPerm.create(perm.user, new_repo, perm_obj)
517 UserRepoToPerm.create(perm.user, new_repo, perm_obj)
518
518
519 for perm in group_perms:
519 for perm in group_perms:
520 perm_name = perm.permission.permission_name.replace(
520 perm_name = perm.permission.permission_name.replace(
521 'group.', 'repository.')
521 'group.', 'repository.')
522 perm_obj = Permission.get_by_key(perm_name)
522 perm_obj = Permission.get_by_key(perm_name)
523 UserGroupRepoToPerm.create(
523 UserGroupRepoToPerm.create(
524 perm.users_group, new_repo, perm_obj)
524 perm.users_group, new_repo, perm_obj)
525
525
526 if private:
526 if private:
527 RepoModel(self.sa).grant_user_permission(
527 RepoModel(self.sa).grant_user_permission(
528 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
528 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
529
529
530 else:
530 else:
531 perm_obj = self._create_default_perms(new_repo, private)
531 perm_obj = self._create_default_perms(new_repo, private)
532 self.sa.add(perm_obj)
532 self.sa.add(perm_obj)
533
533
534 # now automatically start following this repository as owner
534 # now automatically start following this repository as owner
535 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
535 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
536 owner.user_id)
536 owner.user_id)
537
537
538 # we need to flush here, in order to check if database won't
538 # we need to flush here, in order to check if database won't
539 # throw any exceptions, create filesystem dirs at the very end
539 # throw any exceptions, create filesystem dirs at the very end
540 self.sa.flush()
540 self.sa.flush()
541 events.trigger(events.RepoCreateEvent(new_repo))
541 events.trigger(events.RepoCreateEvent(new_repo))
542 return new_repo
542 return new_repo
543
543
544 except Exception:
544 except Exception:
545 log.error(traceback.format_exc())
545 log.error(traceback.format_exc())
546 raise
546 raise
547
547
548 def create(self, form_data, cur_user):
548 def create(self, form_data, cur_user):
549 """
549 """
550 Create repository using celery tasks
550 Create repository using celery tasks
551
551
552 :param form_data:
552 :param form_data:
553 :param cur_user:
553 :param cur_user:
554 """
554 """
555 from rhodecode.lib.celerylib import tasks, run_task
555 from rhodecode.lib.celerylib import tasks, run_task
556 return run_task(tasks.create_repo, form_data, cur_user)
556 return run_task(tasks.create_repo, form_data, cur_user)
557
557
558 def update_permissions(self, repo, perm_additions=None, perm_updates=None,
558 def update_permissions(self, repo, perm_additions=None, perm_updates=None,
559 perm_deletions=None, check_perms=True,
559 perm_deletions=None, check_perms=True,
560 cur_user=None):
560 cur_user=None):
561 if not perm_additions:
561 if not perm_additions:
562 perm_additions = []
562 perm_additions = []
563 if not perm_updates:
563 if not perm_updates:
564 perm_updates = []
564 perm_updates = []
565 if not perm_deletions:
565 if not perm_deletions:
566 perm_deletions = []
566 perm_deletions = []
567
567
568 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
568 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
569
569
570 # update permissions
570 # update permissions
571 for member_id, perm, member_type in perm_updates:
571 for member_id, perm, member_type in perm_updates:
572 member_id = int(member_id)
572 member_id = int(member_id)
573 if member_type == 'user':
573 if member_type == 'user':
574 # this updates also current one if found
574 # this updates also current one if found
575 self.grant_user_permission(
575 self.grant_user_permission(
576 repo=repo, user=member_id, perm=perm)
576 repo=repo, user=member_id, perm=perm)
577 else: # set for user group
577 else: # set for user group
578 # check if we have permissions to alter this usergroup
578 # check if we have permissions to alter this usergroup
579 member_name = UserGroup.get(member_id).users_group_name
579 member_name = UserGroup.get(member_id).users_group_name
580 if not check_perms or HasUserGroupPermissionAny(
580 if not check_perms or HasUserGroupPermissionAny(
581 *req_perms)(member_name, user=cur_user):
581 *req_perms)(member_name, user=cur_user):
582 self.grant_user_group_permission(
582 self.grant_user_group_permission(
583 repo=repo, group_name=member_id, perm=perm)
583 repo=repo, group_name=member_id, perm=perm)
584
584
585 # set new permissions
585 # set new permissions
586 for member_id, perm, member_type in perm_additions:
586 for member_id, perm, member_type in perm_additions:
587 member_id = int(member_id)
587 member_id = int(member_id)
588 if member_type == 'user':
588 if member_type == 'user':
589 self.grant_user_permission(
589 self.grant_user_permission(
590 repo=repo, user=member_id, perm=perm)
590 repo=repo, user=member_id, perm=perm)
591 else: # set for user group
591 else: # set for user group
592 # check if we have permissions to alter this usergroup
592 # check if we have permissions to alter this usergroup
593 member_name = UserGroup.get(member_id).users_group_name
593 member_name = UserGroup.get(member_id).users_group_name
594 if not check_perms or HasUserGroupPermissionAny(
594 if not check_perms or HasUserGroupPermissionAny(
595 *req_perms)(member_name, user=cur_user):
595 *req_perms)(member_name, user=cur_user):
596 self.grant_user_group_permission(
596 self.grant_user_group_permission(
597 repo=repo, group_name=member_id, perm=perm)
597 repo=repo, group_name=member_id, perm=perm)
598
598
599 # delete permissions
599 # delete permissions
600 for member_id, perm, member_type in perm_deletions:
600 for member_id, perm, member_type in perm_deletions:
601 member_id = int(member_id)
601 member_id = int(member_id)
602 if member_type == 'user':
602 if member_type == 'user':
603 self.revoke_user_permission(repo=repo, user=member_id)
603 self.revoke_user_permission(repo=repo, user=member_id)
604 else: # set for user group
604 else: # set for user group
605 # check if we have permissions to alter this usergroup
605 # check if we have permissions to alter this usergroup
606 member_name = UserGroup.get(member_id).users_group_name
606 member_name = UserGroup.get(member_id).users_group_name
607 if not check_perms or HasUserGroupPermissionAny(
607 if not check_perms or HasUserGroupPermissionAny(
608 *req_perms)(member_name, user=cur_user):
608 *req_perms)(member_name, user=cur_user):
609 self.revoke_user_group_permission(
609 self.revoke_user_group_permission(
610 repo=repo, group_name=member_id)
610 repo=repo, group_name=member_id)
611
611
612 def create_fork(self, form_data, cur_user):
612 def create_fork(self, form_data, cur_user):
613 """
613 """
614 Simple wrapper into executing celery task for fork creation
614 Simple wrapper into executing celery task for fork creation
615
615
616 :param form_data:
616 :param form_data:
617 :param cur_user:
617 :param cur_user:
618 """
618 """
619 from rhodecode.lib.celerylib import tasks, run_task
619 from rhodecode.lib.celerylib import tasks, run_task
620 return run_task(tasks.create_repo_fork, form_data, cur_user)
620 return run_task(tasks.create_repo_fork, form_data, cur_user)
621
621
622 def delete(self, repo, forks=None, fs_remove=True, cur_user=None):
622 def delete(self, repo, forks=None, fs_remove=True, cur_user=None):
623 """
623 """
624 Delete given repository, forks parameter defines what do do with
624 Delete given repository, forks parameter defines what do do with
625 attached forks. Throws AttachedForksError if deleted repo has attached
625 attached forks. Throws AttachedForksError if deleted repo has attached
626 forks
626 forks
627
627
628 :param repo:
628 :param repo:
629 :param forks: str 'delete' or 'detach'
629 :param forks: str 'delete' or 'detach'
630 :param fs_remove: remove(archive) repo from filesystem
630 :param fs_remove: remove(archive) repo from filesystem
631 """
631 """
632 if not cur_user:
632 if not cur_user:
633 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
633 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
634 repo = self._get_repo(repo)
634 repo = self._get_repo(repo)
635 if repo:
635 if repo:
636 if forks == 'detach':
636 if forks == 'detach':
637 for r in repo.forks:
637 for r in repo.forks:
638 r.fork = None
638 r.fork = None
639 self.sa.add(r)
639 self.sa.add(r)
640 elif forks == 'delete':
640 elif forks == 'delete':
641 for r in repo.forks:
641 for r in repo.forks:
642 self.delete(r, forks='delete')
642 self.delete(r, forks='delete')
643 elif [f for f in repo.forks]:
643 elif [f for f in repo.forks]:
644 raise AttachedForksError()
644 raise AttachedForksError()
645
645
646 old_repo_dict = repo.get_dict()
646 old_repo_dict = repo.get_dict()
647 events.trigger(events.RepoPreDeleteEvent(repo))
647 events.trigger(events.RepoPreDeleteEvent(repo))
648 try:
648 try:
649 self.sa.delete(repo)
649 self.sa.delete(repo)
650 if fs_remove:
650 if fs_remove:
651 self._delete_filesystem_repo(repo)
651 self._delete_filesystem_repo(repo)
652 else:
652 else:
653 log.debug('skipping removal from filesystem')
653 log.debug('skipping removal from filesystem')
654 old_repo_dict.update({
654 old_repo_dict.update({
655 'deleted_by': cur_user,
655 'deleted_by': cur_user,
656 'deleted_on': time.time(),
656 'deleted_on': time.time(),
657 })
657 })
658 log_delete_repository(**old_repo_dict)
658 log_delete_repository(**old_repo_dict)
659 events.trigger(events.RepoDeleteEvent(repo))
659 events.trigger(events.RepoDeleteEvent(repo))
660 except Exception:
660 except Exception:
661 log.error(traceback.format_exc())
661 log.error(traceback.format_exc())
662 raise
662 raise
663
663
664 def grant_user_permission(self, repo, user, perm):
664 def grant_user_permission(self, repo, user, perm):
665 """
665 """
666 Grant permission for user on given repository, or update existing one
666 Grant permission for user on given repository, or update existing one
667 if found
667 if found
668
668
669 :param repo: Instance of Repository, repository_id, or repository name
669 :param repo: Instance of Repository, repository_id, or repository name
670 :param user: Instance of User, user_id or username
670 :param user: Instance of User, user_id or username
671 :param perm: Instance of Permission, or permission_name
671 :param perm: Instance of Permission, or permission_name
672 """
672 """
673 user = self._get_user(user)
673 user = self._get_user(user)
674 repo = self._get_repo(repo)
674 repo = self._get_repo(repo)
675 permission = self._get_perm(perm)
675 permission = self._get_perm(perm)
676
676
677 # check if we have that permission already
677 # check if we have that permission already
678 obj = self.sa.query(UserRepoToPerm) \
678 obj = self.sa.query(UserRepoToPerm) \
679 .filter(UserRepoToPerm.user == user) \
679 .filter(UserRepoToPerm.user == user) \
680 .filter(UserRepoToPerm.repository == repo) \
680 .filter(UserRepoToPerm.repository == repo) \
681 .scalar()
681 .scalar()
682 if obj is None:
682 if obj is None:
683 # create new !
683 # create new !
684 obj = UserRepoToPerm()
684 obj = UserRepoToPerm()
685 obj.repository = repo
685 obj.repository = repo
686 obj.user = user
686 obj.user = user
687 obj.permission = permission
687 obj.permission = permission
688 self.sa.add(obj)
688 self.sa.add(obj)
689 log.debug('Granted perm %s to %s on %s', perm, user, repo)
689 log.debug('Granted perm %s to %s on %s', perm, user, repo)
690 action_logger_generic(
690 action_logger_generic(
691 'granted permission: {} to user: {} on repo: {}'.format(
691 'granted permission: {} to user: {} on repo: {}'.format(
692 perm, user, repo), namespace='security.repo')
692 perm, user, repo), namespace='security.repo')
693 return obj
693 return obj
694
694
695 def revoke_user_permission(self, repo, user):
695 def revoke_user_permission(self, repo, user):
696 """
696 """
697 Revoke permission for user on given repository
697 Revoke permission for user on given repository
698
698
699 :param repo: Instance of Repository, repository_id, or repository name
699 :param repo: Instance of Repository, repository_id, or repository name
700 :param user: Instance of User, user_id or username
700 :param user: Instance of User, user_id or username
701 """
701 """
702
702
703 user = self._get_user(user)
703 user = self._get_user(user)
704 repo = self._get_repo(repo)
704 repo = self._get_repo(repo)
705
705
706 obj = self.sa.query(UserRepoToPerm) \
706 obj = self.sa.query(UserRepoToPerm) \
707 .filter(UserRepoToPerm.repository == repo) \
707 .filter(UserRepoToPerm.repository == repo) \
708 .filter(UserRepoToPerm.user == user) \
708 .filter(UserRepoToPerm.user == user) \
709 .scalar()
709 .scalar()
710 if obj:
710 if obj:
711 self.sa.delete(obj)
711 self.sa.delete(obj)
712 log.debug('Revoked perm on %s on %s', repo, user)
712 log.debug('Revoked perm on %s on %s', repo, user)
713 action_logger_generic(
713 action_logger_generic(
714 'revoked permission from user: {} on repo: {}'.format(
714 'revoked permission from user: {} on repo: {}'.format(
715 user, repo), namespace='security.repo')
715 user, repo), namespace='security.repo')
716
716
717 def grant_user_group_permission(self, repo, group_name, perm):
717 def grant_user_group_permission(self, repo, group_name, perm):
718 """
718 """
719 Grant permission for user group on given repository, or update
719 Grant permission for user group on given repository, or update
720 existing one if found
720 existing one if found
721
721
722 :param repo: Instance of Repository, repository_id, or repository name
722 :param repo: Instance of Repository, repository_id, or repository name
723 :param group_name: Instance of UserGroup, users_group_id,
723 :param group_name: Instance of UserGroup, users_group_id,
724 or user group name
724 or user group name
725 :param perm: Instance of Permission, or permission_name
725 :param perm: Instance of Permission, or permission_name
726 """
726 """
727 repo = self._get_repo(repo)
727 repo = self._get_repo(repo)
728 group_name = self._get_user_group(group_name)
728 group_name = self._get_user_group(group_name)
729 permission = self._get_perm(perm)
729 permission = self._get_perm(perm)
730
730
731 # check if we have that permission already
731 # check if we have that permission already
732 obj = self.sa.query(UserGroupRepoToPerm) \
732 obj = self.sa.query(UserGroupRepoToPerm) \
733 .filter(UserGroupRepoToPerm.users_group == group_name) \
733 .filter(UserGroupRepoToPerm.users_group == group_name) \
734 .filter(UserGroupRepoToPerm.repository == repo) \
734 .filter(UserGroupRepoToPerm.repository == repo) \
735 .scalar()
735 .scalar()
736
736
737 if obj is None:
737 if obj is None:
738 # create new
738 # create new
739 obj = UserGroupRepoToPerm()
739 obj = UserGroupRepoToPerm()
740
740
741 obj.repository = repo
741 obj.repository = repo
742 obj.users_group = group_name
742 obj.users_group = group_name
743 obj.permission = permission
743 obj.permission = permission
744 self.sa.add(obj)
744 self.sa.add(obj)
745 log.debug('Granted perm %s to %s on %s', perm, group_name, repo)
745 log.debug('Granted perm %s to %s on %s', perm, group_name, repo)
746 action_logger_generic(
746 action_logger_generic(
747 'granted permission: {} to usergroup: {} on repo: {}'.format(
747 'granted permission: {} to usergroup: {} on repo: {}'.format(
748 perm, group_name, repo), namespace='security.repo')
748 perm, group_name, repo), namespace='security.repo')
749
749
750 return obj
750 return obj
751
751
752 def revoke_user_group_permission(self, repo, group_name):
752 def revoke_user_group_permission(self, repo, group_name):
753 """
753 """
754 Revoke permission for user group on given repository
754 Revoke permission for user group on given repository
755
755
756 :param repo: Instance of Repository, repository_id, or repository name
756 :param repo: Instance of Repository, repository_id, or repository name
757 :param group_name: Instance of UserGroup, users_group_id,
757 :param group_name: Instance of UserGroup, users_group_id,
758 or user group name
758 or user group name
759 """
759 """
760 repo = self._get_repo(repo)
760 repo = self._get_repo(repo)
761 group_name = self._get_user_group(group_name)
761 group_name = self._get_user_group(group_name)
762
762
763 obj = self.sa.query(UserGroupRepoToPerm) \
763 obj = self.sa.query(UserGroupRepoToPerm) \
764 .filter(UserGroupRepoToPerm.repository == repo) \
764 .filter(UserGroupRepoToPerm.repository == repo) \
765 .filter(UserGroupRepoToPerm.users_group == group_name) \
765 .filter(UserGroupRepoToPerm.users_group == group_name) \
766 .scalar()
766 .scalar()
767 if obj:
767 if obj:
768 self.sa.delete(obj)
768 self.sa.delete(obj)
769 log.debug('Revoked perm to %s on %s', repo, group_name)
769 log.debug('Revoked perm to %s on %s', repo, group_name)
770 action_logger_generic(
770 action_logger_generic(
771 'revoked permission from usergroup: {} on repo: {}'.format(
771 'revoked permission from usergroup: {} on repo: {}'.format(
772 group_name, repo), namespace='security.repo')
772 group_name, repo), namespace='security.repo')
773
773
774 def delete_stats(self, repo_name):
774 def delete_stats(self, repo_name):
775 """
775 """
776 removes stats for given repo
776 removes stats for given repo
777
777
778 :param repo_name:
778 :param repo_name:
779 """
779 """
780 repo = self._get_repo(repo_name)
780 repo = self._get_repo(repo_name)
781 try:
781 try:
782 obj = self.sa.query(Statistics) \
782 obj = self.sa.query(Statistics) \
783 .filter(Statistics.repository == repo).scalar()
783 .filter(Statistics.repository == repo).scalar()
784 if obj:
784 if obj:
785 self.sa.delete(obj)
785 self.sa.delete(obj)
786 except Exception:
786 except Exception:
787 log.error(traceback.format_exc())
787 log.error(traceback.format_exc())
788 raise
788 raise
789
789
790 def add_repo_field(self, repo_name, field_key, field_label, field_value='',
790 def add_repo_field(self, repo_name, field_key, field_label, field_value='',
791 field_type='str', field_desc=''):
791 field_type='str', field_desc=''):
792
792
793 repo = self._get_repo(repo_name)
793 repo = self._get_repo(repo_name)
794
794
795 new_field = RepositoryField()
795 new_field = RepositoryField()
796 new_field.repository = repo
796 new_field.repository = repo
797 new_field.field_key = field_key
797 new_field.field_key = field_key
798 new_field.field_type = field_type # python type
798 new_field.field_type = field_type # python type
799 new_field.field_value = field_value
799 new_field.field_value = field_value
800 new_field.field_desc = field_desc
800 new_field.field_desc = field_desc
801 new_field.field_label = field_label
801 new_field.field_label = field_label
802 self.sa.add(new_field)
802 self.sa.add(new_field)
803 return new_field
803 return new_field
804
804
805 def delete_repo_field(self, repo_name, field_key):
805 def delete_repo_field(self, repo_name, field_key):
806 repo = self._get_repo(repo_name)
806 repo = self._get_repo(repo_name)
807 field = RepositoryField.get_by_key_name(field_key, repo)
807 field = RepositoryField.get_by_key_name(field_key, repo)
808 if field:
808 if field:
809 self.sa.delete(field)
809 self.sa.delete(field)
810
810
811 def _create_filesystem_repo(self, repo_name, repo_type, repo_group,
811 def _create_filesystem_repo(self, repo_name, repo_type, repo_group,
812 clone_uri=None, repo_store_location=None,
812 clone_uri=None, repo_store_location=None,
813 use_global_config=False):
813 use_global_config=False):
814 """
814 """
815 makes repository on filesystem. It's group aware means it'll create
815 makes repository on filesystem. It's group aware means it'll create
816 a repository within a group, and alter the paths accordingly of
816 a repository within a group, and alter the paths accordingly of
817 group location
817 group location
818
818
819 :param repo_name:
819 :param repo_name:
820 :param alias:
820 :param alias:
821 :param parent:
821 :param parent:
822 :param clone_uri:
822 :param clone_uri:
823 :param repo_store_location:
823 :param repo_store_location:
824 """
824 """
825 from rhodecode.lib.utils import is_valid_repo, is_valid_repo_group
825 from rhodecode.lib.utils import is_valid_repo, is_valid_repo_group
826 from rhodecode.model.scm import ScmModel
826 from rhodecode.model.scm import ScmModel
827
827
828 if Repository.NAME_SEP in repo_name:
828 if Repository.NAME_SEP in repo_name:
829 raise ValueError(
829 raise ValueError(
830 'repo_name must not contain groups got `%s`' % repo_name)
830 'repo_name must not contain groups got `%s`' % repo_name)
831
831
832 if isinstance(repo_group, RepoGroup):
832 if isinstance(repo_group, RepoGroup):
833 new_parent_path = os.sep.join(repo_group.full_path_splitted)
833 new_parent_path = os.sep.join(repo_group.full_path_splitted)
834 else:
834 else:
835 new_parent_path = repo_group or ''
835 new_parent_path = repo_group or ''
836
836
837 if repo_store_location:
837 if repo_store_location:
838 _paths = [repo_store_location]
838 _paths = [repo_store_location]
839 else:
839 else:
840 _paths = [self.repos_path, new_parent_path, repo_name]
840 _paths = [self.repos_path, new_parent_path, repo_name]
841 # we need to make it str for mercurial
841 # we need to make it str for mercurial
842 repo_path = os.path.join(*map(lambda x: safe_str(x), _paths))
842 repo_path = os.path.join(*map(lambda x: safe_str(x), _paths))
843
843
844 # check if this path is not a repository
844 # check if this path is not a repository
845 if is_valid_repo(repo_path, self.repos_path):
845 if is_valid_repo(repo_path, self.repos_path):
846 raise Exception('This path %s is a valid repository' % repo_path)
846 raise Exception('This path %s is a valid repository' % repo_path)
847
847
848 # check if this path is a group
848 # check if this path is a group
849 if is_valid_repo_group(repo_path, self.repos_path):
849 if is_valid_repo_group(repo_path, self.repos_path):
850 raise Exception('This path %s is a valid group' % repo_path)
850 raise Exception('This path %s is a valid group' % repo_path)
851
851
852 log.info('creating repo %s in %s from url: `%s`',
852 log.info('creating repo %s in %s from url: `%s`',
853 repo_name, safe_unicode(repo_path),
853 repo_name, safe_unicode(repo_path),
854 obfuscate_url_pw(clone_uri))
854 obfuscate_url_pw(clone_uri))
855
855
856 backend = get_backend(repo_type)
856 backend = get_backend(repo_type)
857
857
858 config_repo = None if use_global_config else repo_name
858 config_repo = None if use_global_config else repo_name
859 if config_repo and new_parent_path:
859 if config_repo and new_parent_path:
860 config_repo = Repository.NAME_SEP.join(
860 config_repo = Repository.NAME_SEP.join(
861 (new_parent_path, config_repo))
861 (new_parent_path, config_repo))
862 config = make_db_config(clear_session=False, repo=config_repo)
862 config = make_db_config(clear_session=False, repo=config_repo)
863 config.set('extensions', 'largefiles', '')
863 config.set('extensions', 'largefiles', '')
864
864
865 # patch and reset hooks section of UI config to not run any
865 # patch and reset hooks section of UI config to not run any
866 # hooks on creating remote repo
866 # hooks on creating remote repo
867 config.clear_section('hooks')
867 config.clear_section('hooks')
868
868
869 # TODO: johbo: Unify this, hardcoded "bare=True" does not look nice
869 # TODO: johbo: Unify this, hardcoded "bare=True" does not look nice
870 if repo_type == 'git':
870 if repo_type == 'git':
871 repo = backend(
871 repo = backend(
872 repo_path, config=config, create=True, src_url=clone_uri,
872 repo_path, config=config, create=True, src_url=clone_uri,
873 bare=True)
873 bare=True)
874 else:
874 else:
875 repo = backend(
875 repo = backend(
876 repo_path, config=config, create=True, src_url=clone_uri)
876 repo_path, config=config, create=True, src_url=clone_uri)
877
877
878 ScmModel().install_hooks(repo, repo_type=repo_type)
878 ScmModel().install_hooks(repo, repo_type=repo_type)
879
879
880 log.debug('Created repo %s with %s backend',
880 log.debug('Created repo %s with %s backend',
881 safe_unicode(repo_name), safe_unicode(repo_type))
881 safe_unicode(repo_name), safe_unicode(repo_type))
882 return repo
882 return repo
883
883
884 def _rename_filesystem_repo(self, old, new):
884 def _rename_filesystem_repo(self, old, new):
885 """
885 """
886 renames repository on filesystem
886 renames repository on filesystem
887
887
888 :param old: old name
888 :param old: old name
889 :param new: new name
889 :param new: new name
890 """
890 """
891 log.info('renaming repo from %s to %s', old, new)
891 log.info('renaming repo from %s to %s', old, new)
892
892
893 old_path = os.path.join(self.repos_path, old)
893 old_path = os.path.join(self.repos_path, old)
894 new_path = os.path.join(self.repos_path, new)
894 new_path = os.path.join(self.repos_path, new)
895 if os.path.isdir(new_path):
895 if os.path.isdir(new_path):
896 raise Exception(
896 raise Exception(
897 'Was trying to rename to already existing dir %s' % new_path
897 'Was trying to rename to already existing dir %s' % new_path
898 )
898 )
899 shutil.move(old_path, new_path)
899 shutil.move(old_path, new_path)
900
900
901 def _delete_filesystem_repo(self, repo):
901 def _delete_filesystem_repo(self, repo):
902 """
902 """
903 removes repo from filesystem, the removal is acctually made by
903 removes repo from filesystem, the removal is acctually made by
904 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
904 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
905 repository is no longer valid for rhodecode, can be undeleted later on
905 repository is no longer valid for rhodecode, can be undeleted later on
906 by reverting the renames on this repository
906 by reverting the renames on this repository
907
907
908 :param repo: repo object
908 :param repo: repo object
909 """
909 """
910 rm_path = os.path.join(self.repos_path, repo.repo_name)
910 rm_path = os.path.join(self.repos_path, repo.repo_name)
911 repo_group = repo.group
911 repo_group = repo.group
912 log.info("Removing repository %s", rm_path)
912 log.info("Removing repository %s", rm_path)
913 # disable hg/git internal that it doesn't get detected as repo
913 # disable hg/git internal that it doesn't get detected as repo
914 alias = repo.repo_type
914 alias = repo.repo_type
915
915
916 config = make_db_config(clear_session=False)
916 config = make_db_config(clear_session=False)
917 config.set('extensions', 'largefiles', '')
917 config.set('extensions', 'largefiles', '')
918 bare = getattr(repo.scm_instance(config=config), 'bare', False)
918 bare = getattr(repo.scm_instance(config=config), 'bare', False)
919
919
920 # skip this for bare git repos
920 # skip this for bare git repos
921 if not bare:
921 if not bare:
922 # disable VCS repo
922 # disable VCS repo
923 vcs_path = os.path.join(rm_path, '.%s' % alias)
923 vcs_path = os.path.join(rm_path, '.%s' % alias)
924 if os.path.exists(vcs_path):
924 if os.path.exists(vcs_path):
925 shutil.move(vcs_path, os.path.join(rm_path, 'rm__.%s' % alias))
925 shutil.move(vcs_path, os.path.join(rm_path, 'rm__.%s' % alias))
926
926
927 _now = datetime.now()
927 _now = datetime.now()
928 _ms = str(_now.microsecond).rjust(6, '0')
928 _ms = str(_now.microsecond).rjust(6, '0')
929 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
929 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
930 repo.just_name)
930 repo.just_name)
931 if repo_group:
931 if repo_group:
932 # if repository is in group, prefix the removal path with the group
932 # if repository is in group, prefix the removal path with the group
933 args = repo_group.full_path_splitted + [_d]
933 args = repo_group.full_path_splitted + [_d]
934 _d = os.path.join(*args)
934 _d = os.path.join(*args)
935
935
936 if os.path.isdir(rm_path):
936 if os.path.isdir(rm_path):
937 shutil.move(rm_path, os.path.join(self.repos_path, _d))
937 shutil.move(rm_path, os.path.join(self.repos_path, _d))
938
938
939
939
940 class ReadmeFinder:
940 class ReadmeFinder:
941 """
941 """
942 Utility which knows how to find a readme for a specific commit.
942 Utility which knows how to find a readme for a specific commit.
943
943
944 The main idea is that this is a configurable algorithm. When creating an
944 The main idea is that this is a configurable algorithm. When creating an
945 instance you can define parameters, currently only the `default_renderer`.
945 instance you can define parameters, currently only the `default_renderer`.
946 Based on this configuration the method :meth:`search` behaves slightly
946 Based on this configuration the method :meth:`search` behaves slightly
947 different.
947 different.
948 """
948 """
949
949
950 readme_re = re.compile(r'^readme(\.[^\.]+)?$', re.IGNORECASE)
950 readme_re = re.compile(r'^readme(\.[^\.]+)?$', re.IGNORECASE)
951 path_re = re.compile(r'^docs?', re.IGNORECASE)
951 path_re = re.compile(r'^docs?', re.IGNORECASE)
952
952
953 default_priorities = {
953 default_priorities = {
954 None: 0,
954 None: 0,
955 '.text': 2,
955 '.text': 2,
956 '.txt': 3,
956 '.txt': 3,
957 '.rst': 1,
957 '.rst': 1,
958 '.rest': 2,
958 '.rest': 2,
959 '.md': 1,
959 '.md': 1,
960 '.mkdn': 2,
960 '.mkdn': 2,
961 '.mdown': 3,
961 '.mdown': 3,
962 '.markdown': 4,
962 '.markdown': 4,
963 }
963 }
964
964
965 path_priority = {
965 path_priority = {
966 'doc': 0,
966 'doc': 0,
967 'docs': 1,
967 'docs': 1,
968 }
968 }
969
969
970 FALLBACK_PRIORITY = 99
970 FALLBACK_PRIORITY = 99
971
971
972 RENDERER_TO_EXTENSION = {
972 RENDERER_TO_EXTENSION = {
973 'rst': ['.rst', '.rest'],
973 'rst': ['.rst', '.rest'],
974 'markdown': ['.md', 'mkdn', '.mdown', '.markdown'],
974 'markdown': ['.md', 'mkdn', '.mdown', '.markdown'],
975 }
975 }
976
976
977 def __init__(self, default_renderer=None):
977 def __init__(self, default_renderer=None):
978 self._default_renderer = default_renderer
978 self._default_renderer = default_renderer
979 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(
979 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(
980 default_renderer, [])
980 default_renderer, [])
981
981
982 def search(self, commit, path='/'):
982 def search(self, commit, path='/'):
983 """
983 """
984 Find a readme in the given `commit`.
984 Find a readme in the given `commit`.
985 """
985 """
986 nodes = commit.get_nodes(path)
986 nodes = commit.get_nodes(path)
987 matches = self._match_readmes(nodes)
987 matches = self._match_readmes(nodes)
988 matches = self._sort_according_to_priority(matches)
988 matches = self._sort_according_to_priority(matches)
989 if matches:
989 if matches:
990 return matches[0].node
990 return matches[0].node
991
991
992 paths = self._match_paths(nodes)
992 paths = self._match_paths(nodes)
993 paths = self._sort_paths_according_to_priority(paths)
993 paths = self._sort_paths_according_to_priority(paths)
994 for path in paths:
994 for path in paths:
995 match = self.search(commit, path=path)
995 match = self.search(commit, path=path)
996 if match:
996 if match:
997 return match
997 return match
998
998
999 return None
999 return None
1000
1000
1001 def _match_readmes(self, nodes):
1001 def _match_readmes(self, nodes):
1002 for node in nodes:
1002 for node in nodes:
1003 if not node.is_file():
1003 if not node.is_file():
1004 continue
1004 continue
1005 path = node.path.rsplit('/', 1)[-1]
1005 path = node.path.rsplit('/', 1)[-1]
1006 match = self.readme_re.match(path)
1006 match = self.readme_re.match(path)
1007 if match:
1007 if match:
1008 extension = match.group(1)
1008 extension = match.group(1)
1009 yield ReadmeMatch(node, match, self._priority(extension))
1009 yield ReadmeMatch(node, match, self._priority(extension))
1010
1010
1011 def _match_paths(self, nodes):
1011 def _match_paths(self, nodes):
1012 for node in nodes:
1012 for node in nodes:
1013 if not node.is_dir():
1013 if not node.is_dir():
1014 continue
1014 continue
1015 match = self.path_re.match(node.path)
1015 match = self.path_re.match(node.path)
1016 if match:
1016 if match:
1017 yield node.path
1017 yield node.path
1018
1018
1019 def _priority(self, extension):
1019 def _priority(self, extension):
1020 renderer_priority = (
1020 renderer_priority = (
1021 0 if extension in self._renderer_extensions else 1)
1021 0 if extension in self._renderer_extensions else 1)
1022 extension_priority = self.default_priorities.get(
1022 extension_priority = self.default_priorities.get(
1023 extension, self.FALLBACK_PRIORITY)
1023 extension, self.FALLBACK_PRIORITY)
1024 return (renderer_priority, extension_priority)
1024 return (renderer_priority, extension_priority)
1025
1025
1026 def _sort_according_to_priority(self, matches):
1026 def _sort_according_to_priority(self, matches):
1027
1027
1028 def priority_and_path(match):
1028 def priority_and_path(match):
1029 return (match.priority, match.path)
1029 return (match.priority, match.path)
1030
1030
1031 return sorted(matches, key=priority_and_path)
1031 return sorted(matches, key=priority_and_path)
1032
1032
1033 def _sort_paths_according_to_priority(self, paths):
1033 def _sort_paths_according_to_priority(self, paths):
1034
1034
1035 def priority_and_path(path):
1035 def priority_and_path(path):
1036 return (self.path_priority.get(path, self.FALLBACK_PRIORITY), path)
1036 return (self.path_priority.get(path, self.FALLBACK_PRIORITY), path)
1037
1037
1038 return sorted(paths, key=priority_and_path)
1038 return sorted(paths, key=priority_and_path)
1039
1039
1040
1040
1041 class ReadmeMatch:
1041 class ReadmeMatch:
1042
1042
1043 def __init__(self, node, match, priority):
1043 def __init__(self, node, match, priority):
1044 self.node = node
1044 self.node = node
1045 self._match = match
1045 self._match = match
1046 self.priority = priority
1046 self.priority = priority
1047
1047
1048 @property
1048 @property
1049 def path(self):
1049 def path(self):
1050 return self.node.path
1050 return self.node.path
1051
1051
1052 def __repr__(self):
1052 def __repr__(self):
1053 return '<ReadmeMatch {} priority={}'.format(self.path, self.priority)
1053 return '<ReadmeMatch {} priority={}'.format(self.path, self.priority)
General Comments 0
You need to be logged in to leave comments. Login now