##// END OF EJS Templates
models: major update for python3,...
super-admin -
r5070:175fe6cb default
parent child Browse files
Show More
@@ -178,13 +178,14 b' class ChangesetStatusModel(BaseModel):'
178 """
178 """
179
179
180 def group_rule(element):
180 def group_rule(element):
181 review_obj = element[0]
181 _review_obj = element[0]
182 rule_data = review_obj.rule_user_group_data()
182 rule_data = _review_obj.rule_user_group_data()
183 if rule_data and rule_data['id']:
183 if rule_data and rule_data['id']:
184 return rule_data['id']
184 return rule_data['id']
185 # don't return None, as we cant compare this
186 return 0
185
187
186 voting_groups = itertools.groupby(
188 voting_groups = itertools.groupby(sorted(statuses_by_reviewers, key=group_rule), group_rule)
187 sorted(statuses_by_reviewers, key=group_rule), group_rule)
188
189
189 voting_by_groups = [(x, list(y)) for x, y in voting_groups]
190 voting_by_groups = [(x, list(y)) for x, y in voting_groups]
190
191
@@ -296,9 +296,6 b' class CommentsModel(BaseModel):'
296 :param extra_recipients: list of extra users to be added to recipients
296 :param extra_recipients: list of extra users to be added to recipients
297 """
297 """
298
298
299 if not text:
300 log.warning('Missing text for comment, skipping...')
301 return
302 request = get_current_request()
299 request = get_current_request()
303 _ = request.translate
300 _ = request.translate
304
301
@@ -31,7 +31,7 b' import shutil'
31 from pyramid.threadlocal import get_current_request
31 from pyramid.threadlocal import get_current_request
32
32
33 from rhodecode.lib.utils2 import (
33 from rhodecode.lib.utils2 import (
34 safe_unicode, unique_id, safe_int, time_to_datetime, AttributeDict)
34 unique_id, safe_int, safe_str, time_to_datetime, AttributeDict)
35 from rhodecode.lib.ext_json import json
35 from rhodecode.lib.ext_json import json
36 from rhodecode.lib.vcs import VCSError
36 from rhodecode.lib.vcs import VCSError
37 from rhodecode.model import BaseModel
37 from rhodecode.model import BaseModel
@@ -121,7 +121,7 b' class GistModel(BaseModel):'
121 :param gist_acl_level: acl level for this gist
121 :param gist_acl_level: acl level for this gist
122 """
122 """
123 owner = self._get_user(owner)
123 owner = self._get_user(owner)
124 gist_id = safe_unicode(gist_id or unique_id(20))
124 gist_id = safe_str(gist_id or unique_id(20))
125 lifetime = safe_int(lifetime, -1)
125 lifetime = safe_int(lifetime, -1)
126 gist_expires = time.time() + (lifetime * 60) if lifetime != -1 else -1
126 gist_expires = time.time() + (lifetime * 60) if lifetime != -1 else -1
127 expiration = (time_to_datetime(gist_expires)
127 expiration = (time_to_datetime(gist_expires)
@@ -133,13 +133,13 b' class GistModel(BaseModel):'
133 gist.gist_access_id = gist_id
133 gist.gist_access_id = gist_id
134 gist.gist_owner = owner.user_id
134 gist.gist_owner = owner.user_id
135 gist.gist_expires = gist_expires
135 gist.gist_expires = gist_expires
136 gist.gist_type = safe_unicode(gist_type)
136 gist.gist_type = safe_str(gist_type)
137 gist.acl_level = gist_acl_level
137 gist.acl_level = gist_acl_level
138 self.sa.add(gist)
138 self.sa.add(gist)
139 self.sa.flush()
139 self.sa.flush()
140 if gist_type == Gist.GIST_PUBLIC:
140 if gist_type == Gist.GIST_PUBLIC:
141 # use DB ID for easy to use GIST ID
141 # use DB ID for easy to use GIST ID
142 gist_id = safe_unicode(gist.gist_id)
142 gist_id = safe_str(gist.gist_id)
143 gist.gist_access_id = gist_id
143 gist.gist_access_id = gist_id
144 self.sa.add(gist)
144 self.sa.add(gist)
145
145
@@ -152,7 +152,7 b' class GistModel(BaseModel):'
152 # now create single multifile commit
152 # now create single multifile commit
153 message = 'added file'
153 message = 'added file'
154 message += 's: ' if len(gist_mapping) > 1 else ': '
154 message += 's: ' if len(gist_mapping) > 1 else ': '
155 message += ', '.join([x for x in gist_mapping])
155 message += ', '.join([safe_str(x) for x in gist_mapping])
156
156
157 # fake RhodeCode Repository object
157 # fake RhodeCode Repository object
158 fake_repo = AttributeDict({
158 fake_repo = AttributeDict({
@@ -218,7 +218,7 b' class GistModel(BaseModel):'
218
218
219 message = 'updated file'
219 message = 'updated file'
220 message += 's: ' if len(gist_mapping) > 1 else ': '
220 message += 's: ' if len(gist_mapping) > 1 else ': '
221 message += ', '.join([x for x in gist_mapping])
221 message += ', '.join([safe_str(x) for x in gist_mapping])
222
222
223 # fake RhodeCode Repository object
223 # fake RhodeCode Repository object
224 fake_repo = AttributeDict({
224 fake_repo = AttributeDict({
@@ -28,12 +28,11 b' import logging'
28
28
29 from sqlalchemy import or_, and_
29 from sqlalchemy import or_, and_
30
30
31 import rhodecode
32 from rhodecode import events
31 from rhodecode import events
33 from rhodecode.integrations.types.base import EEIntegration
32 from rhodecode.integrations.types.base import EEIntegration
34 from rhodecode.lib.caching_query import FromCache
33 from rhodecode.lib.caching_query import FromCache
35 from rhodecode.model import BaseModel
34 from rhodecode.model import BaseModel
36 from rhodecode.model.db import Integration, Repository, RepoGroup, true, false, case
35 from rhodecode.model.db import Integration, Repository, RepoGroup, true, false, case, null
37 from rhodecode.integrations import integration_type_registry
36 from rhodecode.integrations import integration_type_registry
38
37
39 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
@@ -53,8 +52,7 b' class IntegrationModel(BaseModel):'
53 raise Exception('integration must be int or Instance'
52 raise Exception('integration must be int or Instance'
54 ' of Integration got %s' % type(integration))
53 ' of Integration got %s' % type(integration))
55
54
56 def create(self, IntegrationType, name, enabled, repo, repo_group,
55 def create(self, IntegrationType, name, enabled, repo, repo_group, child_repos_only, settings):
57 child_repos_only, settings):
58 """ Create an IntegrationType integration """
56 """ Create an IntegrationType integration """
59 integration = Integration()
57 integration = Integration()
60 integration.integration_type = IntegrationType.key
58 integration.integration_type = IntegrationType.key
@@ -163,15 +161,15 b' class IntegrationModel(BaseModel):'
163 )
161 )
164
162
165 global_integrations_filter = and_(
163 global_integrations_filter = and_(
166 Integration.repo_id == None,
164 Integration.repo_id == null(),
167 Integration.repo_group_id == None,
165 Integration.repo_group_id == null(),
168 Integration.child_repos_only == false(),
166 Integration.child_repos_only == false(),
169 )
167 )
170
168
171 if isinstance(event, events.RepoEvent):
169 if isinstance(event, events.RepoEvent):
172 root_repos_integrations_filter = and_(
170 root_repos_integrations_filter = and_(
173 Integration.repo_id == None,
171 Integration.repo_id == null(),
174 Integration.repo_group_id == None,
172 Integration.repo_group_id == null(),
175 Integration.child_repos_only == true(),
173 Integration.child_repos_only == true(),
176 )
174 )
177
175
@@ -225,7 +223,7 b' class IntegrationModel(BaseModel):'
225 query = query.order_by(order_by_criterion)
223 query = query.order_by(order_by_criterion)
226
224
227 if cache:
225 if cache:
228 cache_key = "get_enabled_repo_integrations_%i" % event.repo.repo_id
226 cache_key = f"get_enabled_repo_integrations_{event.repo.repo_id}"
229 query = query.options(
227 query = query.options(
230 FromCache("sql_cache_short", cache_key))
228 FromCache("sql_cache_short", cache_key))
231 else: # only global integrations
229 else: # only global integrations
@@ -332,6 +332,7 b" EMAIL_COMMENT_FILE_SUBJECT_TEMPLATE = ''"
332 import cssutils
332 import cssutils
333 # hijack css utils logger and replace with ours
333 # hijack css utils logger and replace with ours
334 log = logging.getLogger('rhodecode.cssutils.premailer')
334 log = logging.getLogger('rhodecode.cssutils.premailer')
335 log.setLevel(logging.INFO)
335 cssutils.log.setLog(log)
336 cssutils.log.setLog(log)
336
337
337
338
@@ -377,7 +378,10 b' class EmailNotificationModel(BaseModel):'
377 'rhodecode:templates/email_templates/pull_request_update.mako',
378 'rhodecode:templates/email_templates/pull_request_update.mako',
378 }
379 }
379
380
380 premailer_instance = premailer.Premailer()
381 premailer_instance = premailer.Premailer(
382 #cssutils_logging_handler=log.handlers[0],
383 #cssutils_logging_level=logging.INFO
384 )
381
385
382 def __init__(self):
386 def __init__(self):
383 """
387 """
@@ -180,16 +180,24 b' class PermissionModel(BaseModel):'
180
180
181 def _make_new_user_perm(self, user, perm_name):
181 def _make_new_user_perm(self, user, perm_name):
182 log.debug('Creating new user permission:%s', perm_name)
182 log.debug('Creating new user permission:%s', perm_name)
183 new_perm = Permission.get_by_key(perm_name)
184 if not new_perm:
185 raise ValueError(f'permission with name {perm_name} not found')
186
183 new = UserToPerm()
187 new = UserToPerm()
184 new.user = user
188 new.user = user
185 new.permission = Permission.get_by_key(perm_name)
189 new.permission = new_perm
186 return new
190 return new
187
191
188 def _make_new_user_group_perm(self, user_group, perm_name):
192 def _make_new_user_group_perm(self, user_group, perm_name):
189 log.debug('Creating new user group permission:%s', perm_name)
193 log.debug('Creating new user group permission:%s', perm_name)
194 new_perm = Permission.get_by_key(perm_name)
195 if not new_perm:
196 raise ValueError(f'permission with name {perm_name} not found')
197
190 new = UserGroupToPerm()
198 new = UserGroupToPerm()
191 new.users_group = user_group
199 new.users_group = user_group
192 new.permission = Permission.get_by_key(perm_name)
200 new.permission = new_perm
193 return new
201 return new
194
202
195 def _keep_perm(self, perm_name, keep_fields):
203 def _keep_perm(self, perm_name, keep_fields):
@@ -278,10 +286,10 b' class PermissionModel(BaseModel):'
278 raise ValueError('Missing permission for %s' % (_perm_key,))
286 raise ValueError('Missing permission for %s' % (_perm_key,))
279
287
280 if obj_type == 'user':
288 if obj_type == 'user':
281 p = self._make_new_user_perm(object, perm_value)
289 p = self._make_new_user_perm(to_object, perm_value)
282 self.sa.add(p)
290 self.sa.add(p)
283 if obj_type == 'user_group':
291 if obj_type == 'user_group':
284 p = self._make_new_user_group_perm(object, perm_value)
292 p = self._make_new_user_group_perm(to_object, perm_value)
285 self.sa.add(p)
293 self.sa.add(p)
286
294
287 def _set_new_user_perms(self, user, form_result, preserve=None):
295 def _set_new_user_perms(self, user, form_result, preserve=None):
@@ -321,8 +329,8 b' class PermissionModel(BaseModel):'
321 def _get_group(perm_name):
329 def _get_group(perm_name):
322 return '.'.join(perm_name.split('.')[:1])
330 return '.'.join(perm_name.split('.')[:1])
323
331
324 defined_perms_groups = map(
332 defined_perms_groups = list(map(
325 _get_group, (x.permission.permission_name for x in obj_perms))
333 _get_group, (x.permission.permission_name for x in obj_perms)))
326 log.debug('GOT ALREADY DEFINED:%s', obj_perms)
334 log.debug('GOT ALREADY DEFINED:%s', obj_perms)
327
335
328 if force:
336 if force:
@@ -23,15 +23,16 b''
23 pull request model for RhodeCode
23 pull request model for RhodeCode
24 """
24 """
25
25
26
27 import json
28 import logging
26 import logging
29 import os
27 import os
30
28
31 import datetime
29 import datetime
32 import urllib.request, urllib.parse, urllib.error
30 import urllib.request
31 import urllib.parse
32 import urllib.error
33 import collections
33 import collections
34
34
35 import dataclasses as dataclasses
35 from pyramid.threadlocal import get_current_request
36 from pyramid.threadlocal import get_current_request
36
37
37 from rhodecode.lib.vcs.nodes import FileNode
38 from rhodecode.lib.vcs.nodes import FileNode
@@ -40,11 +41,12 b' from rhodecode.lib import helpers as h, '
40 from rhodecode.lib import audit_logger
41 from rhodecode.lib import audit_logger
41 from collections import OrderedDict
42 from collections import OrderedDict
42 from rhodecode.lib.hooks_daemon import prepare_callback_daemon
43 from rhodecode.lib.hooks_daemon import prepare_callback_daemon
44 from rhodecode.lib.ext_json import sjson as json
43 from rhodecode.lib.markup_renderer import (
45 from rhodecode.lib.markup_renderer import (
44 DEFAULT_COMMENTS_RENDERER, RstTemplateRenderer)
46 DEFAULT_COMMENTS_RENDERER, RstTemplateRenderer)
45 from rhodecode.lib.utils2 import (
47 from rhodecode.lib.hash_utils import md5_safe
46 safe_unicode, safe_str, md5_safe, AttributeDict, safe_int,
48 from rhodecode.lib.str_utils import safe_str
47 get_current_rhodecode_user)
49 from rhodecode.lib.utils2 import AttributeDict, get_current_rhodecode_user
48 from rhodecode.lib.vcs.backends.base import (
50 from rhodecode.lib.vcs.backends.base import (
49 Reference, MergeResponse, MergeFailureReason, UpdateFailureReason,
51 Reference, MergeResponse, MergeFailureReason, UpdateFailureReason,
50 TargetRefMissing, SourceRefMissing)
52 TargetRefMissing, SourceRefMissing)
@@ -55,7 +57,7 b' from rhodecode.model import BaseModel'
55 from rhodecode.model.changeset_status import ChangesetStatusModel
57 from rhodecode.model.changeset_status import ChangesetStatusModel
56 from rhodecode.model.comment import CommentsModel
58 from rhodecode.model.comment import CommentsModel
57 from rhodecode.model.db import (
59 from rhodecode.model.db import (
58 aliased, null, lazyload, and_, or_, func, String, cast, PullRequest, PullRequestReviewers, ChangesetStatus,
60 aliased, null, lazyload, and_, or_, select, func, String, cast, PullRequest, PullRequestReviewers, ChangesetStatus,
59 PullRequestVersion, ChangesetComment, Repository, RepoReviewRule, User)
61 PullRequestVersion, ChangesetComment, Repository, RepoReviewRule, User)
60 from rhodecode.model.meta import Session
62 from rhodecode.model.meta import Session
61 from rhodecode.model.notification import NotificationModel, \
63 from rhodecode.model.notification import NotificationModel, \
@@ -116,9 +118,8 b' def get_diff_info('
116 source_scm.get_diff(commit1=source_commit, commit2=target_commit,
118 source_scm.get_diff(commit1=source_commit, commit2=target_commit,
117 ignore_whitespace=False, context=3)
119 ignore_whitespace=False, context=3)
118
120
119 diff_processor = diffs.DiffProcessor(
121 diff_processor = diffs.DiffProcessor(vcs_diff, diff_format='newdiff',
120 vcs_diff, format='newdiff', diff_limit=None,
122 diff_limit=0, file_limit=0, show_full_diff=True)
121 file_limit=None, show_full_diff=True)
122
123
123 _parsed = diff_processor.prepare()
124 _parsed = diff_processor.prepare()
124
125
@@ -317,7 +318,7 b' class PullRequestModel(BaseModel):'
317 q = PullRequest.query()
318 q = PullRequest.query()
318
319
319 if search_q:
320 if search_q:
320 like_expression = u'%{}%'.format(safe_unicode(search_q))
321 like_expression = u'%{}%'.format(safe_str(search_q))
321 q = q.join(User, User.user_id == PullRequest.user_id)
322 q = q.join(User, User.user_id == PullRequest.user_id)
322 q = q.filter(or_(
323 q = q.filter(or_(
323 cast(PullRequest.pull_request_id, String).ilike(like_expression),
324 cast(PullRequest.pull_request_id, String).ilike(like_expression),
@@ -489,7 +490,7 b' class PullRequestModel(BaseModel):'
489 q = q.filter(pull_request_alias.status.in_(statuses))
490 q = q.filter(pull_request_alias.status.in_(statuses))
490
491
491 if search_q:
492 if search_q:
492 like_expression = u'%{}%'.format(safe_unicode(search_q))
493 like_expression = u'%{}%'.format(safe_str(search_q))
493 q = q.join(User, User.user_id == pull_request_alias.user_id)
494 q = q.join(User, User.user_id == pull_request_alias.user_id)
494 q = q.filter(or_(
495 q = q.filter(or_(
495 cast(pull_request_alias.pull_request_id, String).ilike(like_expression),
496 cast(pull_request_alias.pull_request_id, String).ilike(like_expression),
@@ -562,12 +563,14 b' class PullRequestModel(BaseModel):'
562 """
563 """
563 q = PullRequest.query()
564 q = PullRequest.query()
564 if user_id:
565 if user_id:
565 reviewers_subquery = Session().query(
566
566 PullRequestReviewers.pull_request_id).filter(
567 base_query = select(PullRequestReviewers)\
567 PullRequestReviewers.user_id == user_id).subquery()
568 .where(PullRequestReviewers.user_id == user_id)\
569 .with_only_columns(PullRequestReviewers.pull_request_id)
570
568 user_filter = or_(
571 user_filter = or_(
569 PullRequest.user_id == user_id,
572 PullRequest.user_id == user_id,
570 PullRequest.pull_request_id.in_(reviewers_subquery)
573 PullRequest.pull_request_id.in_(base_query)
571 )
574 )
572 q = PullRequest.query().filter(user_filter)
575 q = PullRequest.query().filter(user_filter)
573
576
@@ -576,7 +579,7 b' class PullRequestModel(BaseModel):'
576 q = q.filter(PullRequest.status.in_(statuses))
579 q = q.filter(PullRequest.status.in_(statuses))
577
580
578 if query:
581 if query:
579 like_expression = u'%{}%'.format(safe_unicode(query))
582 like_expression = u'%{}%'.format(safe_str(query))
580 q = q.join(User, User.user_id == PullRequest.user_id)
583 q = q.join(User, User.user_id == PullRequest.user_id)
581 q = q.filter(or_(
584 q = q.filter(or_(
582 cast(PullRequest.pull_request_id, String).ilike(like_expression),
585 cast(PullRequest.pull_request_id, String).ilike(like_expression),
@@ -656,7 +659,7 b' class PullRequestModel(BaseModel):'
656 q = q.filter(pull_request_alias.status.in_(statuses))
659 q = q.filter(pull_request_alias.status.in_(statuses))
657
660
658 if query:
661 if query:
659 like_expression = u'%{}%'.format(safe_unicode(query))
662 like_expression = u'%{}%'.format(safe_str(query))
660 q = q.join(User, User.user_id == pull_request_alias.user_id)
663 q = q.join(User, User.user_id == pull_request_alias.user_id)
661 q = q.filter(or_(
664 q = q.filter(or_(
662 cast(pull_request_alias.pull_request_id, String).ilike(like_expression),
665 cast(pull_request_alias.pull_request_id, String).ilike(like_expression),
@@ -939,7 +942,8 b' class PullRequestModel(BaseModel):'
939
942
940 def merge_repo(self, pull_request, user, extras):
943 def merge_repo(self, pull_request, user, extras):
941 repo_type = pull_request.source_repo.repo_type
944 repo_type = pull_request.source_repo.repo_type
942 log.debug("Merging pull request %s", pull_request.pull_request_id)
945 log.debug("Merging pull request %s", pull_request)
946
943 extras['user_agent'] = '{}/internal-merge'.format(repo_type)
947 extras['user_agent'] = '{}/internal-merge'.format(repo_type)
944 merge_state = self._merge_pull_request(pull_request, user, extras)
948 merge_state = self._merge_pull_request(pull_request, user, extras)
945 if merge_state.executed:
949 if merge_state.executed:
@@ -952,14 +956,14 b' class PullRequestModel(BaseModel):'
952 user, pull_request)
956 user, pull_request)
953
957
954 else:
958 else:
955 log.warn("Merge failed, not updating the pull request.")
959 log.warning("Merge failed, not updating the pull request.")
956 return merge_state
960 return merge_state
957
961
958 def _merge_pull_request(self, pull_request, user, extras, merge_msg=None):
962 def _merge_pull_request(self, pull_request, user, extras, merge_msg=None):
959 target_vcs = pull_request.target_repo.scm_instance()
963 target_vcs = pull_request.target_repo.scm_instance()
960 source_vcs = pull_request.source_repo.scm_instance()
964 source_vcs = pull_request.source_repo.scm_instance()
961
965
962 message = safe_unicode(merge_msg or vcs_settings.MERGE_MESSAGE_TMPL).format(
966 message = safe_str(merge_msg or vcs_settings.MERGE_MESSAGE_TMPL).format(
963 pr_id=pull_request.pull_request_id,
967 pr_id=pull_request.pull_request_id,
964 pr_title=pull_request.title,
968 pr_title=pull_request.title,
965 pr_desc=pull_request.description,
969 pr_desc=pull_request.description,
@@ -995,6 +999,7 b' class PullRequestModel(BaseModel):'
995 user_name=user_name, user_email=user.email,
999 user_name=user_name, user_email=user.email,
996 message=message, use_rebase=use_rebase,
1000 message=message, use_rebase=use_rebase,
997 close_branch=close_branch)
1001 close_branch=close_branch)
1002
998 return merge_state
1003 return merge_state
999
1004
1000 def _comment_and_close_pr(self, pull_request, user, merge_state, close_msg=None):
1005 def _comment_and_close_pr(self, pull_request, user, merge_state, close_msg=None):
@@ -1003,7 +1008,7 b' class PullRequestModel(BaseModel):'
1003 close_msg = close_msg or 'Pull request merged and closed'
1008 close_msg = close_msg or 'Pull request merged and closed'
1004
1009
1005 CommentsModel().create(
1010 CommentsModel().create(
1006 text=safe_unicode(close_msg),
1011 text=safe_str(close_msg),
1007 repo=pull_request.target_repo.repo_id,
1012 repo=pull_request.target_repo.repo_id,
1008 user=user.user_id,
1013 user=user.user_id,
1009 pull_request=pull_request.pull_request_id,
1014 pull_request=pull_request.pull_request_id,
@@ -1289,9 +1294,10 b' class PullRequestModel(BaseModel):'
1289 source_repo, source_ref_id, target_ref_id,
1294 source_repo, source_ref_id, target_ref_id,
1290 hide_whitespace_changes=hide_whitespace_changes, diff_context=diff_context)
1295 hide_whitespace_changes=hide_whitespace_changes, diff_context=diff_context)
1291
1296
1292 old_diff_data = diffs.DiffProcessor(old_diff)
1297 # NOTE: this was using diff_format='gitdiff'
1298 old_diff_data = diffs.DiffProcessor(old_diff, diff_format='newdiff')
1293 old_diff_data.prepare()
1299 old_diff_data.prepare()
1294 new_diff_data = diffs.DiffProcessor(new_diff)
1300 new_diff_data = diffs.DiffProcessor(new_diff, diff_format='newdiff')
1295 new_diff_data.prepare()
1301 new_diff_data.prepare()
1296
1302
1297 return old_diff_data, new_diff_data
1303 return old_diff_data, new_diff_data
@@ -1598,7 +1604,7 b' class PullRequestModel(BaseModel):'
1598 return None
1604 return None
1599 else:
1605 else:
1600 pr_url = urllib.parse.unquote(self.get_url(pull_request, request=request))
1606 pr_url = urllib.parse.unquote(self.get_url(pull_request, request=request))
1601 return safe_unicode('{pr_url}/repository'.format(pr_url=pr_url))
1607 return safe_str('{pr_url}/repository'.format(pr_url=pr_url))
1602
1608
1603 def _notify_reviewers(self, pull_request, user_ids, role, user):
1609 def _notify_reviewers(self, pull_request, user_ids, role, user):
1604 # notification to reviewers/observers
1610 # notification to reviewers/observers
@@ -2032,8 +2038,8 b' class PullRequestModel(BaseModel):'
2032 _ = translator or get_current_request().translate
2038 _ = translator or get_current_request().translate
2033
2039
2034 commit_id = safe_str(commit_id) if commit_id else None
2040 commit_id = safe_str(commit_id) if commit_id else None
2035 branch = safe_unicode(branch) if branch else None
2041 branch = safe_str(branch) if branch else None
2036 bookmark = safe_unicode(bookmark) if bookmark else None
2042 bookmark = safe_str(bookmark) if bookmark else None
2037
2043
2038 selected = None
2044 selected = None
2039
2045
@@ -2072,8 +2078,8 b' class PullRequestModel(BaseModel):'
2072 u'No commit refs could be found matching: {}'.format(ref))
2078 u'No commit refs could be found matching: {}'.format(ref))
2073 elif repo.DEFAULT_BRANCH_NAME in repo.branches:
2079 elif repo.DEFAULT_BRANCH_NAME in repo.branches:
2074 selected = u'branch:{}:{}'.format(
2080 selected = u'branch:{}:{}'.format(
2075 safe_unicode(repo.DEFAULT_BRANCH_NAME),
2081 safe_str(repo.DEFAULT_BRANCH_NAME),
2076 safe_unicode(repo.branches[repo.DEFAULT_BRANCH_NAME])
2082 safe_str(repo.branches[repo.DEFAULT_BRANCH_NAME])
2077 )
2083 )
2078 elif repo.commit_ids:
2084 elif repo.commit_ids:
2079 # make the user select in this case
2085 # make the user select in this case
@@ -2113,7 +2119,7 b' class PullRequestModel(BaseModel):'
2113 log.debug('calculating diff between '
2119 log.debug('calculating diff between '
2114 'source_ref:%s and target_ref:%s for repo `%s`',
2120 'source_ref:%s and target_ref:%s for repo `%s`',
2115 target_ref_id, source_ref_id,
2121 target_ref_id, source_ref_id,
2116 safe_unicode(vcs_repo.path))
2122 safe_str(vcs_repo.path))
2117
2123
2118 vcs_diff = vcs_repo.get_diff(
2124 vcs_diff = vcs_repo.get_diff(
2119 commit1=target_commit, commit2=source_commit,
2125 commit1=target_commit, commit2=source_commit,
@@ -2373,8 +2379,16 b' class MergeCheck(object):'
2373 return merge_details
2379 return merge_details
2374
2380
2375
2381
2376 ChangeTuple = collections.namedtuple(
2382 @dataclasses.dataclass
2377 'ChangeTuple', ['added', 'common', 'removed', 'total'])
2383 class ChangeTuple:
2384 added: list
2385 common: list
2386 removed: list
2387 total: list
2378
2388
2379 FileChangeTuple = collections.namedtuple(
2389
2380 'FileChangeTuple', ['added', 'modified', 'removed'])
2390 @dataclasses.dataclass
2391 class FileChangeTuple:
2392 added: list
2393 modified: list
2394 removed: list
@@ -26,6 +26,7 b' import traceback'
26 import datetime
26 import datetime
27
27
28 from pyramid.threadlocal import get_current_request
28 from pyramid.threadlocal import get_current_request
29 from sqlalchemy.orm import aliased
29 from zope.cachedescriptors.property import Lazy as LazyProperty
30 from zope.cachedescriptors.property import Lazy as LazyProperty
30
31
31 from rhodecode import events
32 from rhodecode import events
@@ -36,7 +37,7 b' from rhodecode.lib import hooks_base'
36 from rhodecode.lib.user_log_filter import user_log_filter
37 from rhodecode.lib.user_log_filter import user_log_filter
37 from rhodecode.lib.utils import make_db_config
38 from rhodecode.lib.utils import make_db_config
38 from rhodecode.lib.utils2 import (
39 from rhodecode.lib.utils2 import (
39 safe_str, safe_unicode, remove_prefix, obfuscate_url_pw,
40 safe_str, remove_prefix, obfuscate_url_pw,
40 get_current_rhodecode_user, safe_int, action_logger_generic)
41 get_current_rhodecode_user, safe_int, action_logger_generic)
41 from rhodecode.lib.vcs.backends import get_backend
42 from rhodecode.lib.vcs.backends import get_backend
42 from rhodecode.model import BaseModel
43 from rhodecode.model import BaseModel
@@ -78,7 +79,7 b' class RepoModel(BaseModel):'
78 repo_to_perm.permission = Permission.get_by_key(default_perm)
79 repo_to_perm.permission = Permission.get_by_key(default_perm)
79
80
80 repo_to_perm.repository = repository
81 repo_to_perm.repository = repository
81 repo_to_perm.user_id = def_user.user_id
82 repo_to_perm.user = def_user
82
83
83 return repo_to_perm
84 return repo_to_perm
84
85
@@ -112,7 +113,7 b' class RepoModel(BaseModel):'
112 def _extract_id_from_repo_name(self, repo_name):
113 def _extract_id_from_repo_name(self, repo_name):
113 if repo_name.startswith('/'):
114 if repo_name.startswith('/'):
114 repo_name = repo_name.lstrip('/')
115 repo_name = repo_name.lstrip('/')
115 by_id_match = re.match(r'^_(\d{1,})', repo_name)
116 by_id_match = re.match(r'^_(\d+)', repo_name)
116 if by_id_match:
117 if by_id_match:
117 return by_id_match.groups()[0]
118 return by_id_match.groups()[0]
118
119
@@ -138,7 +139,7 b' class RepoModel(BaseModel):'
138
139
139 def get_repos_for_root(self, root, traverse=False):
140 def get_repos_for_root(self, root, traverse=False):
140 if traverse:
141 if traverse:
141 like_expression = u'{}%'.format(safe_unicode(root))
142 like_expression = u'{}%'.format(safe_str(root))
142 repos = Repository.query().filter(
143 repos = Repository.query().filter(
143 Repository.repo_name.like(like_expression)).all()
144 Repository.repo_name.like(like_expression)).all()
144 else:
145 else:
@@ -209,12 +210,12 b' class RepoModel(BaseModel):'
209 def quick_menu(repo_name):
210 def quick_menu(repo_name):
210 return _render('quick_menu', repo_name)
211 return _render('quick_menu', repo_name)
211
212
212 def repo_lnk(name, rtype, rstate, private, archived, fork_of):
213 def repo_lnk(name, rtype, rstate, private, archived, fork_repo_name):
213 if short_name is not None:
214 if short_name is not None:
214 short_name_var = short_name
215 short_name_var = short_name
215 else:
216 else:
216 short_name_var = not admin
217 short_name_var = not admin
217 return _render('repo_name', name, rtype, rstate, private, archived, fork_of,
218 return _render('repo_name', name, rtype, rstate, private, archived, fork_repo_name,
218 short_name=short_name_var, admin=False)
219 short_name=short_name_var, admin=False)
219
220
220 def last_change(last_change):
221 def last_change(last_change):
@@ -259,7 +260,7 b' class RepoModel(BaseModel):'
259 "menu": quick_menu(repo.repo_name),
260 "menu": quick_menu(repo.repo_name),
260
261
261 "name": repo_lnk(repo.repo_name, repo.repo_type, repo.repo_state,
262 "name": repo_lnk(repo.repo_name, repo.repo_type, repo.repo_state,
262 repo.private, repo.archived, repo.fork),
263 repo.private, repo.archived, repo.fork_repo_name),
263
264
264 "desc": desc(h.escape(repo.description)),
265 "desc": desc(h.escape(repo.description)),
265
266
@@ -268,7 +269,7 b' class RepoModel(BaseModel):'
268 "last_changeset": last_rev(repo.repo_name, changeset_cache),
269 "last_changeset": last_rev(repo.repo_name, changeset_cache),
269 "last_changeset_raw": changeset_cache.get('revision'),
270 "last_changeset_raw": changeset_cache.get('revision'),
270
271
271 "owner": user_profile(repo.User.username),
272 "owner": user_profile(repo.owner_username),
272
273
273 "state": state(repo.repo_state),
274 "state": state(repo.repo_state),
274 "rss": rss_lnk(repo.repo_name),
275 "rss": rss_lnk(repo.repo_name),
@@ -309,6 +310,8 b' class RepoModel(BaseModel):'
309 ) \
310 ) \
310 .count()
311 .count()
311
312
313 RepoFork = aliased(Repository)
314 OwnerUser = aliased(User)
312 base_q = Session.query(
315 base_q = Session.query(
313 Repository.repo_id,
316 Repository.repo_id,
314 Repository.repo_name,
317 Repository.repo_name,
@@ -317,18 +320,18 b' class RepoModel(BaseModel):'
317 Repository.repo_state,
320 Repository.repo_state,
318 Repository.private,
321 Repository.private,
319 Repository.archived,
322 Repository.archived,
320 Repository.fork,
321 Repository.updated_on,
323 Repository.updated_on,
322 Repository._changeset_cache,
324 Repository._changeset_cache,
323 User,
325 RepoFork.repo_name.label('fork_repo_name'),
326 OwnerUser.username.label('owner_username'),
324 ) \
327 ) \
325 .filter(Repository.group_id == repo_group_id) \
328 .filter(Repository.group_id == repo_group_id) \
326 .filter(or_(
329 .filter(or_(
327 # generate multiple IN to fix limitation problems
330 # generate multiple IN to fix limitation problems
328 *in_filter_generator(Repository.repo_id, allowed_ids))
331 *in_filter_generator(Repository.repo_id, allowed_ids))
329 ) \
332 ) \
330 .join(User, User.user_id == Repository.user_id) \
333 .outerjoin(RepoFork, Repository.fork_id == RepoFork.repo_id) \
331 .group_by(Repository, User)
334 .join(OwnerUser, Repository.user_id == OwnerUser.user_id)
332
335
333 repos_data_total_filtered_count = base_q.count()
336 repos_data_total_filtered_count = base_q.count()
334
337
@@ -515,8 +518,8 b' class RepoModel(BaseModel):'
515 landing_rev = landing_rev or default_landing_ref
518 landing_rev = landing_rev or default_landing_ref
516
519
517 try:
520 try:
518 repo_name = safe_unicode(repo_name)
521 repo_name = safe_str(repo_name)
519 description = safe_unicode(description)
522 description = safe_str(description)
520 # repo name is just a name of repository
523 # repo name is just a name of repository
521 # while repo_name_full is a full qualified name that is combined
524 # while repo_name_full is a full qualified name that is combined
522 # with name and path of group
525 # with name and path of group
@@ -979,14 +982,14 b' class RepoModel(BaseModel):'
979
982
980 # check if this path is not a repository
983 # check if this path is not a repository
981 if is_valid_repo(repo_path, self.repos_path):
984 if is_valid_repo(repo_path, self.repos_path):
982 raise Exception('This path %s is a valid repository' % repo_path)
985 raise Exception(f'This path {repo_path} is a valid repository')
983
986
984 # check if this path is a group
987 # check if this path is a group
985 if is_valid_repo_group(repo_path, self.repos_path):
988 if is_valid_repo_group(repo_path, self.repos_path):
986 raise Exception('This path %s is a valid group' % repo_path)
989 raise Exception(f'This path {repo_path} is a valid group')
987
990
988 log.info('creating repo %s in %s from url: `%s`',
991 log.info('creating repo %s in %s from url: `%s`',
989 repo_name, safe_unicode(repo_path),
992 repo_name, safe_str(repo_path),
990 obfuscate_url_pw(clone_uri))
993 obfuscate_url_pw(clone_uri))
991
994
992 backend = get_backend(repo_type)
995 backend = get_backend(repo_type)
@@ -1016,7 +1019,7 b' class RepoModel(BaseModel):'
1016 repo.install_hooks()
1019 repo.install_hooks()
1017
1020
1018 log.debug('Created repo %s with %s backend',
1021 log.debug('Created repo %s with %s backend',
1019 safe_unicode(repo_name), safe_unicode(repo_type))
1022 safe_str(repo_name), safe_str(repo_type))
1020 return repo
1023 return repo
1021
1024
1022 def _rename_filesystem_repo(self, old, new):
1025 def _rename_filesystem_repo(self, old, new):
@@ -1038,8 +1041,8 b' class RepoModel(BaseModel):'
1038
1041
1039 def _delete_filesystem_repo(self, repo):
1042 def _delete_filesystem_repo(self, repo):
1040 """
1043 """
1041 removes repo from filesystem, the removal is acctually made by
1044 removes repo from filesystem, the removal is actually made by
1042 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
1045 added rm__ prefix into dir, and rename internal .hg/.git dirs so this
1043 repository is no longer valid for rhodecode, can be undeleted later on
1046 repository is no longer valid for rhodecode, can be undeleted later on
1044 by reverting the renames on this repository
1047 by reverting the renames on this repository
1045
1048
@@ -1047,7 +1050,7 b' class RepoModel(BaseModel):'
1047 """
1050 """
1048 rm_path = os.path.join(self.repos_path, repo.repo_name)
1051 rm_path = os.path.join(self.repos_path, repo.repo_name)
1049 repo_group = repo.group
1052 repo_group = repo.group
1050 log.info("Removing repository %s", rm_path)
1053 log.info("delete_filesystem_repo: removing repository %s", rm_path)
1051 # disable hg/git internal that it doesn't get detected as repo
1054 # disable hg/git internal that it doesn't get detected as repo
1052 alias = repo.repo_type
1055 alias = repo.repo_type
1053
1056
@@ -1094,19 +1097,19 b' class ReadmeFinder:'
1094 path_re = re.compile(r'^docs?', re.IGNORECASE)
1097 path_re = re.compile(r'^docs?', re.IGNORECASE)
1095
1098
1096 default_priorities = {
1099 default_priorities = {
1097 None: 0,
1100 None: 0,
1098 '.text': 2,
1101 '.rst': 1,
1099 '.txt': 3,
1102 '.md': 1,
1100 '.rst': 1,
1103 '.rest': 2,
1101 '.rest': 2,
1104 '.mkdn': 2,
1102 '.md': 1,
1105 '.text': 2,
1103 '.mkdn': 2,
1106 '.txt': 3,
1104 '.mdown': 3,
1107 '.mdown': 3,
1105 '.markdown': 4,
1108 '.markdown': 4,
1106 }
1109 }
1107
1110
1108 path_priority = {
1111 path_priority = {
1109 'doc': 0,
1112 'doc': 0,
1110 'docs': 1,
1113 'docs': 1,
1111 }
1114 }
1112
1115
@@ -1122,7 +1125,7 b' class ReadmeFinder:'
1122 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(
1125 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(
1123 default_renderer, [])
1126 default_renderer, [])
1124
1127
1125 def search(self, commit, path=u'/'):
1128 def search(self, commit, path='/'):
1126 """
1129 """
1127 Find a readme in the given `commit`.
1130 Find a readme in the given `commit`.
1128 """
1131 """
@@ -131,7 +131,7 b' class RepoGroupModel(BaseModel):'
131 repo_group_to_perm.permission = Permission.get_by_key(default_perm)
131 repo_group_to_perm.permission = Permission.get_by_key(default_perm)
132
132
133 repo_group_to_perm.group = new_group
133 repo_group_to_perm.group = new_group
134 repo_group_to_perm.user_id = def_user.user_id
134 repo_group_to_perm.user = def_user
135 return repo_group_to_perm
135 return repo_group_to_perm
136
136
137 def _get_group_name_and_parent(self, group_name_full, repo_in_path=False,
137 def _get_group_name_and_parent(self, group_name_full, repo_in_path=False,
@@ -780,10 +780,10 b' class RepoGroupModel(BaseModel):'
780 }
780 }
781 if admin:
781 if admin:
782 repo_count = group.repositories.count()
782 repo_count = group.repositories.count()
783 children_groups = map(
783 children_groups = list(map(
784 h.safe_unicode,
784 h.safe_str,
785 itertools.chain((g.name for g in group.parents),
785 itertools.chain((g.name for g in group.parents),
786 (x.name for x in [group])))
786 (x.name for x in [group]))))
787 row.update({
787 row.update({
788 "action": repo_group_actions(
788 "action": repo_group_actions(
789 group.group_id, group.group_name, repo_count),
789 group.group_id, group.group_name, repo_count),
@@ -30,6 +30,7 b' from sqlalchemy import func'
30 from zope.cachedescriptors.property import Lazy as LazyProperty
30 from zope.cachedescriptors.property import Lazy as LazyProperty
31
31
32 import rhodecode
32 import rhodecode
33 from rhodecode.lib.str_utils import safe_bytes
33 from rhodecode.lib.vcs import get_backend
34 from rhodecode.lib.vcs import get_backend
34 from rhodecode.lib.vcs.exceptions import RepositoryError, NodeNotChangedError
35 from rhodecode.lib.vcs.exceptions import RepositoryError, NodeNotChangedError
35 from rhodecode.lib.vcs.nodes import FileNode
36 from rhodecode.lib.vcs.nodes import FileNode
@@ -42,7 +43,7 b' from rhodecode.lib.exceptions import Non'
42 from rhodecode.lib import hooks_utils
43 from rhodecode.lib import hooks_utils
43 from rhodecode.lib.utils import (
44 from rhodecode.lib.utils import (
44 get_filesystem_repos, make_db_config)
45 get_filesystem_repos, make_db_config)
45 from rhodecode.lib.utils2 import (safe_str, safe_unicode)
46 from rhodecode.lib.str_utils import safe_str
46 from rhodecode.lib.system_info import get_system_info
47 from rhodecode.lib.system_info import get_system_info
47 from rhodecode.model import BaseModel
48 from rhodecode.model import BaseModel
48 from rhodecode.model.db import (
49 from rhodecode.model.db import (
@@ -284,8 +285,7 b' class ScmModel(BaseModel):'
284 repo.update_commit_cache(config=config, cs_cache=None)
285 repo.update_commit_cache(config=config, cs_cache=None)
285 if delete:
286 if delete:
286 cache_namespace_uid = 'cache_repo.{}'.format(repo_id)
287 cache_namespace_uid = 'cache_repo.{}'.format(repo_id)
287 rc_cache.clear_cache_namespace(
288 rc_cache.clear_cache_namespace('cache_repo', cache_namespace_uid, method=rc_cache.CLEAR_INVALIDATE)
288 'cache_repo', cache_namespace_uid, invalidate=True)
289
289
290 def toggle_following_repo(self, follow_repo_id, user_id):
290 def toggle_following_repo(self, follow_repo_id, user_id):
291
291
@@ -443,25 +443,18 b' class ScmModel(BaseModel):'
443 raise
443 raise
444
444
445 def commit_change(self, repo, repo_name, commit, user, author, message,
445 def commit_change(self, repo, repo_name, commit, user, author, message,
446 content, f_path):
446 content: bytes, f_path: bytes):
447 """
447 """
448 Commits changes
448 Commits changes
449
450 :param repo: SCM instance
451
452 """
449 """
453 user = self._get_user(user)
450 user = self._get_user(user)
454
451
455 # decoding here will force that we have proper encoded values
456 # in any other case this will throw exceptions and deny commit
457 content = safe_str(content)
458 path = safe_str(f_path)
459 # message and author needs to be unicode
452 # message and author needs to be unicode
460 # proper backend should then translate that into required type
453 # proper backend should then translate that into required type
461 message = safe_unicode(message)
454 message = safe_str(message)
462 author = safe_unicode(author)
455 author = safe_str(author)
463 imc = repo.in_memory_commit
456 imc = repo.in_memory_commit
464 imc.change(FileNode(path, content, mode=commit.get_file_mode(f_path)))
457 imc.change(FileNode(f_path, content, mode=commit.get_file_mode(f_path)))
465 try:
458 try:
466 # TODO: handle pre-push action !
459 # TODO: handle pre-push action !
467 tip = imc.commit(
460 tip = imc.commit(
@@ -480,9 +473,9 b' class ScmModel(BaseModel):'
480 repo_name=repo_name, repo_type=repo.alias, commit_ids=[tip.raw_id])
473 repo_name=repo_name, repo_type=repo.alias, commit_ids=[tip.raw_id])
481 return tip
474 return tip
482
475
483 def _sanitize_path(self, f_path):
476 def _sanitize_path(self, f_path: bytes):
484 if f_path.startswith('/') or f_path.startswith('./') or '../' in f_path:
477 if f_path.startswith(b'/') or f_path.startswith(b'./') or b'../' in f_path:
485 raise NonRelativePathError('%s is not an relative path' % f_path)
478 raise NonRelativePathError(b'%b is not an relative path' % f_path)
486 if f_path:
479 if f_path:
487 f_path = os.path.normpath(f_path)
480 f_path = os.path.normpath(f_path)
488 return f_path
481 return f_path
@@ -531,15 +524,24 b' class ScmModel(BaseModel):'
531 """
524 """
532 _files = list()
525 _files = list()
533 _dirs = list()
526 _dirs = list()
527
534 try:
528 try:
535 _repo = self._get_repo(repo_name)
529 _repo = self._get_repo(repo_name)
536 commit = _repo.scm_instance().get_commit(commit_id=commit_id)
530 commit = _repo.scm_instance().get_commit(commit_id=commit_id)
537 root_path = root_path.lstrip('/')
531 root_path = root_path.lstrip('/')
538 for __, dirs, files in commit.walk(root_path):
532
533 # get RootNode, inject pre-load options before walking
534 top_node = commit.get_node(root_path)
535 extended_info_pre_load = []
536 if extended_info:
537 extended_info_pre_load += ['md5']
538 top_node.default_pre_load = ['is_binary', 'size'] + extended_info_pre_load
539
540 for __, dirs, files in commit.walk(top_node):
539
541
540 for f in files:
542 for f in files:
541 _content = None
543 _content = None
542 _data = f_name = f.unicode_path
544 _data = f_name = f.str_path
543
545
544 if not flat:
546 if not flat:
545 _data = {
547 _data = {
@@ -561,7 +563,7 b' class ScmModel(BaseModel):'
561 and f.size > max_file_bytes)
563 and f.size > max_file_bytes)
562 full_content = None
564 full_content = None
563 if not f.is_binary and not over_size_limit:
565 if not f.is_binary and not over_size_limit:
564 full_content = safe_str(f.content)
566 full_content = f.str_content
565
567
566 _data.update({
568 _data.update({
567 "content": full_content,
569 "content": full_content,
@@ -569,7 +571,7 b' class ScmModel(BaseModel):'
569 _files.append(_data)
571 _files.append(_data)
570
572
571 for d in dirs:
573 for d in dirs:
572 _data = d_name = d.unicode_path
574 _data = d_name = d.str_path
573 if not flat:
575 if not flat:
574 _data = {
576 _data = {
575 "name": h.escape(d_name),
577 "name": h.escape(d_name),
@@ -577,10 +579,10 b' class ScmModel(BaseModel):'
577 }
579 }
578 if extended_info:
580 if extended_info:
579 _data.update({
581 _data.update({
580 "md5": None,
582 "md5": "",
581 "binary": None,
583 "binary": False,
582 "size": None,
584 "size": 0,
583 "extension": None,
585 "extension": "",
584 })
586 })
585 if content:
587 if content:
586 _data.update({
588 _data.update({
@@ -609,7 +611,7 b' class ScmModel(BaseModel):'
609 for f in files:
611 for f in files:
610
612
611 _data = {
613 _data = {
612 "name": h.escape(f.unicode_path),
614 "name": h.escape(f.str_path),
613 "type": "file",
615 "type": "file",
614 }
616 }
615
617
@@ -618,7 +620,7 b' class ScmModel(BaseModel):'
618 for d in dirs:
620 for d in dirs:
619
621
620 _data = {
622 _data = {
621 "name": h.escape(d.unicode_path),
623 "name": h.escape(d.str_path),
622 "type": "dir",
624 "type": "dir",
623 }
625 }
624
626
@@ -634,6 +636,7 b' class ScmModel(BaseModel):'
634 """
636 """
635 retrieve single node from commit
637 retrieve single node from commit
636 """
638 """
639
637 try:
640 try:
638
641
639 _repo = self._get_repo(repo_name)
642 _repo = self._get_repo(repo_name)
@@ -644,7 +647,7 b' class ScmModel(BaseModel):'
644 raise RepositoryError('The given path is a directory')
647 raise RepositoryError('The given path is a directory')
645
648
646 _content = None
649 _content = None
647 f_name = file_node.unicode_path
650 f_name = file_node.str_path
648
651
649 file_data = {
652 file_data = {
650 "name": h.escape(f_name),
653 "name": h.escape(f_name),
@@ -677,7 +680,7 b' class ScmModel(BaseModel):'
677 full_content = None
680 full_content = None
678 all_lines = 0
681 all_lines = 0
679 if not file_node.is_binary and not over_size_limit:
682 if not file_node.is_binary and not over_size_limit:
680 full_content = safe_unicode(file_node.content)
683 full_content = safe_str(file_node.content)
681 all_lines, empty_lines = file_node.count_lines(full_content)
684 all_lines, empty_lines = file_node.count_lines(full_content)
682
685
683 file_data.update({
686 file_data.update({
@@ -693,7 +696,7 b' class ScmModel(BaseModel):'
693 full_content = None
696 full_content = None
694 all_lines = 0
697 all_lines = 0
695 if not is_binary and not over_size_limit:
698 if not is_binary and not over_size_limit:
696 full_content = safe_unicode(_content)
699 full_content = safe_str(_content)
697 all_lines, empty_lines = file_node.count_lines(full_content)
700 all_lines, empty_lines = file_node.count_lines(full_content)
698
701
699 file_data.update({
702 file_data.update({
@@ -718,12 +721,15 b' class ScmModel(BaseModel):'
718 _repo = self._get_repo(repo_name)
721 _repo = self._get_repo(repo_name)
719 commit = _repo.scm_instance().get_commit(commit_id=commit_id)
722 commit = _repo.scm_instance().get_commit(commit_id=commit_id)
720 root_path = root_path.lstrip('/')
723 root_path = root_path.lstrip('/')
721 for __, dirs, files in commit.walk(root_path):
724 top_node = commit.get_node(root_path)
725 top_node.default_pre_load = []
726
727 for __, dirs, files in commit.walk(top_node):
722
728
723 for f in files:
729 for f in files:
724 is_binary, md5, size, _content = f.metadata_uncached()
730 is_binary, md5, size, _content = f.metadata_uncached()
725 _data = {
731 _data = {
726 "name": f.unicode_path,
732 "name": f.str_path,
727 "md5": md5,
733 "md5": md5,
728 "extension": f.extension,
734 "extension": f.extension,
729 "binary": is_binary,
735 "binary": is_binary,
@@ -759,26 +765,9 b' class ScmModel(BaseModel):'
759 user = self._get_user(user)
765 user = self._get_user(user)
760 scm_instance = repo.scm_instance(cache=False)
766 scm_instance = repo.scm_instance(cache=False)
761
767
762 processed_nodes = []
768 message = safe_str(message)
763 for f_path in nodes:
764 f_path = self._sanitize_path(f_path)
765 content = nodes[f_path]['content']
766 f_path = safe_str(f_path)
767 # decoding here will force that we have proper encoded values
768 # in any other case this will throw exceptions and deny commit
769 if isinstance(content, (str,)):
770 content = safe_str(content)
771 elif isinstance(content, (file, cStringIO.OutputType,)):
772 content = content.read()
773 else:
774 raise Exception('Content is of unrecognized type %s' % (
775 type(content)
776 ))
777 processed_nodes.append((f_path, content))
778
779 message = safe_unicode(message)
780 commiter = user.full_contact
769 commiter = user.full_contact
781 author = safe_unicode(author) if author else commiter
770 author = safe_str(author) if author else commiter
782
771
783 imc = scm_instance.in_memory_commit
772 imc = scm_instance.in_memory_commit
784
773
@@ -786,13 +775,39 b' class ScmModel(BaseModel):'
786 parent_commit = EmptyCommit(alias=scm_instance.alias)
775 parent_commit = EmptyCommit(alias=scm_instance.alias)
787
776
788 if isinstance(parent_commit, EmptyCommit):
777 if isinstance(parent_commit, EmptyCommit):
789 # EmptyCommit means we we're editing empty repository
778 # EmptyCommit means we're editing empty repository
790 parents = None
779 parents = None
791 else:
780 else:
792 parents = [parent_commit]
781 parents = [parent_commit]
782
783 upload_file_types = (io.BytesIO, io.BufferedRandom)
784 processed_nodes = []
785 for filename, content_dict in nodes.items():
786 if not isinstance(filename, bytes):
787 raise ValueError(f'filename key in nodes needs to be bytes , or {upload_file_types}')
788 content = content_dict['content']
789 if not isinstance(content, upload_file_types + (bytes,)):
790 raise ValueError('content key value in nodes needs to be bytes')
791
792 for f_path in nodes:
793 f_path = self._sanitize_path(f_path)
794 content = nodes[f_path]['content']
795
796 # decoding here will force that we have proper encoded values
797 # in any other case this will throw exceptions and deny commit
798
799 if isinstance(content, bytes):
800 pass
801 elif isinstance(content, upload_file_types):
802 content = content.read()
803 else:
804 raise Exception(f'Content is of unrecognized type {type(content)}, expected {upload_file_types}')
805 processed_nodes.append((f_path, content))
806
793 # add multiple nodes
807 # add multiple nodes
794 for path, content in processed_nodes:
808 for path, content in processed_nodes:
795 imc.add(FileNode(path, content=content))
809 imc.add(FileNode(path, content=content))
810
796 # TODO: handle pre push scenario
811 # TODO: handle pre push scenario
797 tip = imc.commit(message=message,
812 tip = imc.commit(message=message,
798 author=author,
813 author=author,
@@ -813,9 +828,9 b' class ScmModel(BaseModel):'
813 user = self._get_user(user)
828 user = self._get_user(user)
814 scm_instance = repo.scm_instance(cache=False)
829 scm_instance = repo.scm_instance(cache=False)
815
830
816 message = safe_unicode(message)
831 message = safe_str(message)
817 commiter = user.full_contact
832 commiter = user.full_contact
818 author = safe_unicode(author) if author else commiter
833 author = safe_str(author) if author else commiter
819
834
820 imc = scm_instance.in_memory_commit
835 imc = scm_instance.in_memory_commit
821
836
@@ -897,14 +912,14 b' class ScmModel(BaseModel):'
897 processed_nodes = []
912 processed_nodes = []
898 for f_path in nodes:
913 for f_path in nodes:
899 f_path = self._sanitize_path(f_path)
914 f_path = self._sanitize_path(f_path)
900 # content can be empty but for compatabilty it allows same dicts
915 # content can be empty but for compatibility it allows same dicts
901 # structure as add_nodes
916 # structure as add_nodes
902 content = nodes[f_path].get('content')
917 content = nodes[f_path].get('content')
903 processed_nodes.append((f_path, content))
918 processed_nodes.append((safe_bytes(f_path), content))
904
919
905 message = safe_unicode(message)
920 message = safe_str(message)
906 commiter = user.full_contact
921 commiter = user.full_contact
907 author = safe_unicode(author) if author else commiter
922 author = safe_str(author) if author else commiter
908
923
909 imc = scm_instance.in_memory_commit
924 imc = scm_instance.in_memory_commit
910
925
@@ -994,7 +1009,7 b' class ScmModel(BaseModel):'
994 choices = [default_landing_ref]
1009 choices = [default_landing_ref]
995
1010
996 # branches
1011 # branches
997 branch_group = [(u'branch:%s' % safe_unicode(b), safe_unicode(b)) for b in repo.branches]
1012 branch_group = [(f'branch:{safe_str(b)}', safe_str(b)) for b in repo.branches]
998 if not branch_group:
1013 if not branch_group:
999 # new repo, or without maybe a branch?
1014 # new repo, or without maybe a branch?
1000 branch_group = default_ref_options
1015 branch_group = default_ref_options
@@ -1006,7 +1021,7 b' class ScmModel(BaseModel):'
1006 # bookmarks for HG
1021 # bookmarks for HG
1007 if repo.alias == 'hg':
1022 if repo.alias == 'hg':
1008 bookmarks_group = (
1023 bookmarks_group = (
1009 [(u'book:%s' % safe_unicode(b), safe_unicode(b))
1024 [(f'book:{safe_str(b)}', safe_str(b))
1010 for b in repo.bookmarks],
1025 for b in repo.bookmarks],
1011 _("Bookmarks"))
1026 _("Bookmarks"))
1012 ref_options.append(bookmarks_group)
1027 ref_options.append(bookmarks_group)
@@ -1014,7 +1029,7 b' class ScmModel(BaseModel):'
1014
1029
1015 # tags
1030 # tags
1016 tags_group = (
1031 tags_group = (
1017 [(u'tag:%s' % safe_unicode(t), safe_unicode(t))
1032 [(f'tag:{safe_str(t)}', safe_str(t))
1018 for t in repo.tags],
1033 for t in repo.tags],
1019 _("Tags"))
1034 _("Tags"))
1020 ref_options.append(tags_group)
1035 ref_options.append(tags_group)
@@ -19,16 +19,16 b''
19
19
20 import os
20 import os
21 import re
21 import re
22 import hashlib
23 import logging
22 import logging
24 import time
23 import time
25 import functools
24 import functools
26 import bleach
25 import bleach
27 from collections import namedtuple
26 from collections import namedtuple
28
27
29 from pyramid.threadlocal import get_current_request, get_current_registry
28 from pyramid.threadlocal import get_current_request
30
29
31 from rhodecode.lib import rc_cache
30 from rhodecode.lib import rc_cache
31 from rhodecode.lib.hash_utils import sha1_safe
32 from rhodecode.lib.utils2 import (
32 from rhodecode.lib.utils2 import (
33 Optional, AttributeDict, safe_str, remove_prefix, str2bool)
33 Optional, AttributeDict, safe_str, remove_prefix, str2bool)
34 from rhodecode.lib.vcs.backends import base
34 from rhodecode.lib.vcs.backends import base
@@ -132,10 +132,9 b' class SettingsModel(BaseModel):'
132 if not key:
132 if not key:
133 # keys are unique so they need appended info
133 # keys are unique so they need appended info
134 if self.repo:
134 if self.repo:
135 key = hashlib.sha1(
135 key = sha1_safe(f'{section}{val}{repository_id}')
136 '{}{}{}'.format(section, val, repository_id)).hexdigest()
137 else:
136 else:
138 key = hashlib.sha1('{}{}'.format(section, val)).hexdigest()
137 key = sha1_safe(f'{section}{val}')
139
138
140 new_ui.ui_key = key
139 new_ui.ui_key = key
141
140
@@ -212,30 +211,20 b' class SettingsModel(BaseModel):'
212
211
213 def get_cache_region(self):
212 def get_cache_region(self):
214 repo = self._get_repo(self.repo) if self.repo else None
213 repo = self._get_repo(self.repo) if self.repo else None
215 cache_key = "repo.{}".format(repo.repo_id) if repo else "general_settings"
214 cache_key = f"repo.{repo.repo_id}" if repo else "repo.ALL"
216 cache_namespace_uid = 'cache_settings.{}'.format(cache_key)
215 cache_namespace_uid = f'cache_settings.{cache_key}'
217 region = rc_cache.get_or_create_region('cache_general', cache_namespace_uid)
216 region = rc_cache.get_or_create_region('cache_general', cache_namespace_uid)
218 return region, cache_key
217 return region, cache_namespace_uid
219
220 def invalidate_settings_cache(self):
221 region, cache_key = self.get_cache_region()
222 log.debug('Invalidation cache region %s for cache_key: %s', region, cache_key)
223 region.invalidate()
224
218
225 def get_all_settings(self, cache=False, from_request=True):
219 def invalidate_settings_cache(self, hard=False):
226 # defines if we use GLOBAL, or PER_REPO
220 region, namespace_key = self.get_cache_region()
227 repo = self._get_repo(self.repo) if self.repo else None
221 log.debug('Invalidation cache [%s] region %s for cache_key: %s',
222 'invalidate_settings_cache', region, namespace_key)
228
223
229 # initially try the requests context, this is the fastest
224 # we use hard cleanup if invalidation is sent
230 # we only fetch global config
225 rc_cache.clear_cache_namespace(region, namespace_key, method=rc_cache.CLEAR_DELETE)
231 if from_request:
232 request = get_current_request()
233
226
234 if request and not repo and hasattr(request, 'call_context') and hasattr(request.call_context, 'rc_config'):
227 def get_cache_call_method(self, cache=True):
235 rc_config = request.call_context.rc_config
236 if rc_config:
237 return rc_config
238
239 region, cache_key = self.get_cache_region()
228 region, cache_key = self.get_cache_region()
240
229
241 @region.conditional_cache_on_arguments(condition=cache)
230 @region.conditional_cache_on_arguments(condition=cache)
@@ -245,10 +234,28 b' class SettingsModel(BaseModel):'
245 raise Exception('Could not get application settings !')
234 raise Exception('Could not get application settings !')
246
235
247 settings = {
236 settings = {
248 'rhodecode_' + res.app_settings_name: res.app_settings_value
237 f'rhodecode_{res.app_settings_name}': res.app_settings_value
249 for res in q
238 for res in q
250 }
239 }
251 return settings
240 return settings
241 return _get_all_settings
242
243 def get_all_settings(self, cache=False, from_request=True):
244 # defines if we use GLOBAL, or PER_REPO
245 repo = self._get_repo(self.repo) if self.repo else None
246
247 # initially try the requests context, this is the fastest
248 # we only fetch global config, NOT for repo-specific
249 if from_request and not repo:
250 request = get_current_request()
251
252 if request and hasattr(request, 'call_context') and hasattr(request.call_context, 'rc_config'):
253 rc_config = request.call_context.rc_config
254 if rc_config:
255 return rc_config
256
257 _region, cache_key = self.get_cache_region()
258 _get_all_settings = self.get_cache_call_method(cache=cache)
252
259
253 start = time.time()
260 start = time.time()
254 result = _get_all_settings('rhodecode_settings', cache_key)
261 result = _get_all_settings('rhodecode_settings', cache_key)
@@ -318,8 +325,7 b' class SettingsModel(BaseModel):'
318 def list_enabled_social_plugins(self, settings):
325 def list_enabled_social_plugins(self, settings):
319 enabled = []
326 enabled = []
320 for plug in SOCIAL_PLUGINS_LIST:
327 for plug in SOCIAL_PLUGINS_LIST:
321 if str2bool(settings.get('rhodecode_auth_{}_enabled'.format(plug)
328 if str2bool(settings.get(f'rhodecode_auth_{plug}_enabled')):
322 )):
323 enabled.append(plug)
329 enabled.append(plug)
324 return enabled
330 return enabled
325
331
@@ -28,6 +28,7 b' from cryptography.hazmat.primitives.asym'
28 from cryptography.hazmat.primitives import serialization as crypto_serialization
28 from cryptography.hazmat.primitives import serialization as crypto_serialization
29 from cryptography.hazmat.backends import default_backend as crypto_default_backend
29 from cryptography.hazmat.backends import default_backend as crypto_default_backend
30
30
31 from rhodecode.lib.str_utils import safe_bytes, safe_str
31 from rhodecode.model import BaseModel
32 from rhodecode.model import BaseModel
32 from rhodecode.model.db import UserSshKeys
33 from rhodecode.model.db import UserSshKeys
33 from rhodecode.model.meta import Session
34 from rhodecode.model.meta import Session
@@ -85,10 +86,13 b' class SshKeyModel(BaseModel):'
85 crypto_serialization.Encoding.PEM,
86 crypto_serialization.Encoding.PEM,
86 private_format,
87 private_format,
87 crypto_serialization.NoEncryption())
88 crypto_serialization.NoEncryption())
89 private_key = safe_str(private_key)
90
88 public_key = key.public_key().public_bytes(
91 public_key = key.public_key().public_bytes(
89 crypto_serialization.Encoding.OpenSSH,
92 crypto_serialization.Encoding.OpenSSH,
90 crypto_serialization.PublicFormat.OpenSSH
93 crypto_serialization.PublicFormat.OpenSSH
91 )
94 )
95 public_key = safe_str(public_key)
92
96
93 if comment:
97 if comment:
94 public_key = public_key + " " + comment
98 public_key = public_key + " " + comment
@@ -19,7 +19,9 b''
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 urllib.request, urllib.error, urllib.parse
22 import urllib.request
23 import urllib.error
24 import urllib.parse
23 from packaging.version import Version
25 from packaging.version import Version
24
26
25 import rhodecode
27 import rhodecode
@@ -32,8 +32,9 b' from sqlalchemy.exc import DatabaseError'
32 from rhodecode import events
32 from rhodecode import events
33 from rhodecode.lib.user_log_filter import user_log_filter
33 from rhodecode.lib.user_log_filter import user_log_filter
34 from rhodecode.lib.utils2 import (
34 from rhodecode.lib.utils2 import (
35 safe_unicode, get_current_rhodecode_user, action_logger_generic,
35 get_current_rhodecode_user, action_logger_generic,
36 AttributeDict, str2bool)
36 AttributeDict, str2bool)
37 from rhodecode.lib.str_utils import safe_str
37 from rhodecode.lib.exceptions import (
38 from rhodecode.lib.exceptions import (
38 DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
39 DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
39 UserOwnsUserGroupsException, NotAllowedToCreateUserError,
40 UserOwnsUserGroupsException, NotAllowedToCreateUserError,
@@ -87,7 +88,7 b' class UserModel(BaseModel):'
87 query = query.filter(User.active == true())
88 query = query.filter(User.active == true())
88
89
89 if name_contains:
90 if name_contains:
90 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
91 ilike_expression = u'%{}%'.format(safe_str(name_contains))
91 query = query.filter(
92 query = query.filter(
92 or_(
93 or_(
93 User.name.ilike(ilike_expression),
94 User.name.ilike(ilike_expression),
@@ -358,8 +359,8 b' class UserModel(BaseModel):'
358 new_user.admin = admin
359 new_user.admin = admin
359 new_user.email = email
360 new_user.email = email
360 new_user.active = active
361 new_user.active = active
361 new_user.extern_name = safe_unicode(extern_name)
362 new_user.extern_name = safe_str(extern_name)
362 new_user.extern_type = safe_unicode(extern_type)
363 new_user.extern_type = safe_str(extern_type)
363 new_user.name = firstname
364 new_user.name = firstname
364 new_user.lastname = lastname
365 new_user.lastname = lastname
365 new_user.description = description
366 new_user.description = description
@@ -533,7 +534,7 b' class UserModel(BaseModel):'
533
534
534 left_overs = False
535 left_overs = False
535
536
536 # if nothing is done we have left overs left
537 # if nothing is done we have leftovers left
537 return left_overs
538 return left_overs
538
539
539 def _handle_user_artifacts(self, username, artifacts, handle_user,
540 def _handle_user_artifacts(self, username, artifacts, handle_user,
@@ -909,8 +910,8 b' class UserModel(BaseModel):'
909 ip_range = ip_range.strip()
910 ip_range = ip_range.strip()
910 if '-' in ip_range:
911 if '-' in ip_range:
911 start_ip, end_ip = ip_range.split('-', 1)
912 start_ip, end_ip = ip_range.split('-', 1)
912 start_ip = ipaddress.ip_address(safe_unicode(start_ip.strip()))
913 start_ip = ipaddress.ip_address(safe_str(start_ip.strip()))
913 end_ip = ipaddress.ip_address(safe_unicode(end_ip.strip()))
914 end_ip = ipaddress.ip_address(safe_str(end_ip.strip()))
914 parsed_ip_range = []
915 parsed_ip_range = []
915
916
916 for index in range(int(start_ip), int(end_ip) + 1):
917 for index in range(int(start_ip), int(end_ip) + 1):
@@ -21,7 +21,7 b''
21 import logging
21 import logging
22 import traceback
22 import traceback
23
23
24 from rhodecode.lib.utils2 import safe_str, safe_unicode
24 from rhodecode.lib.utils2 import safe_str
25 from rhodecode.lib.exceptions import (
25 from rhodecode.lib.exceptions import (
26 UserGroupAssignedException, RepoGroupAssignmentError)
26 UserGroupAssignedException, RepoGroupAssignmentError)
27 from rhodecode.lib.utils2 import (
27 from rhodecode.lib.utils2 import (
@@ -58,7 +58,7 b' class UserGroupModel(BaseModel):'
58 user_group_to_perm.permission = Permission.get_by_key(default_perm)
58 user_group_to_perm.permission = Permission.get_by_key(default_perm)
59
59
60 user_group_to_perm.user_group = user_group
60 user_group_to_perm.user_group = user_group
61 user_group_to_perm.user_id = def_user.user_id
61 user_group_to_perm.user = def_user
62 return user_group_to_perm
62 return user_group_to_perm
63
63
64 def update_permissions(
64 def update_permissions(
@@ -710,7 +710,7 b' class UserGroupModel(BaseModel):'
710 query = query.filter(UserGroup.users_group_active == true())
710 query = query.filter(UserGroup.users_group_active == true())
711
711
712 if name_contains:
712 if name_contains:
713 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
713 ilike_expression = u'%{}%'.format(safe_str(name_contains))
714 query = query.filter(
714 query = query.filter(
715 UserGroup.users_group_name.ilike(ilike_expression))\
715 UserGroup.users_group_name.ilike(ilike_expression))\
716 .order_by(func.length(UserGroup.users_group_name))\
716 .order_by(func.length(UserGroup.users_group_name))\
General Comments 0
You need to be logged in to leave comments. Login now