##// END OF EJS Templates
release: merge back stable branch into default
marcink -
r4251:fc351abb merge default
parent child Browse files
Show More

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

@@ -0,0 +1,60 b''
1 |RCE| 4.18.1 |RNS|
2 ------------------
3
4 Release Date
5 ^^^^^^^^^^^^
6
7 - 2020-01-20
8
9
10 New Features
11 ^^^^^^^^^^^^
12
13
14
15 General
16 ^^^^^^^
17
18 - API: invalidate license cache on set_license_key call.
19 - API: add send_email flag for comments api to allow commenting without email notification.
20 - API: added pull requests versions into returned API data.
21 - Dashboard: fixed jumping of text in grid loading by new loading indicator.
22 - Installation: add few extra defaults that makes RhodeCode nicer out of the box.
23 - Pull Requests: small code cleanup to define other type of merge username.
24 RC_MERGE_USER_NAME_ATTR env variable defines what should be used from user as merge username.
25 - Gists: cleanup UI and make the gist access id use monospace according to the new UI.
26
27
28 Security
29 ^^^^^^^^
30
31 - Repository permission: properly flush permission caches on set private mode of repository.
32 Otherwise we get cached values still in place until it expires.
33 - Repository permission: add set/un-set of private repository from permissions page.
34 - Permissions: flush all user permissions in case of default user permission changes.
35
36
37 Performance
38 ^^^^^^^^^^^
39
40 - Caches: used more efficient way of fetching all users for permissions invalidation.
41 - Issue trackers: optimized performance of fetching issue tracker patterns.
42
43
44 Fixes
45 ^^^^^
46
47 - SSH: fixed SSH problems with EE edition.
48 - Branch permissions: remove emtpy tooltips on branch permission entries.
49 - Core: fixed cython compat inspect that caused some API calls to not work correctly in EE release.
50 - Audit logger: use copy of params we later modify to prevent from modification by the store
51 function of parameters that we only use for reading.
52 - Users: fixed wrong mention of readme in user description help block.
53 - Issue trackers: fixed wrong examples in patterns.
54 - Issue trackers: fixed missing option to get back to inherited settings.
55
56
57 Upgrade notes
58 ^^^^^^^^^^^^^
59
60 - Scheduled release addressing problems in 4.18.X releases.
@@ -0,0 +1,49 b''
1 |RCE| 4.18.2 |RNS|
2 ------------------
3
4 Release Date
5 ^^^^^^^^^^^^
6
7 - 2020-01-28
8
9
10 New Features
11 ^^^^^^^^^^^^
12
13
14
15 General
16 ^^^^^^^
17
18 - Permissions: add better help text about default permissions, and correlation with anonymous access enabled.
19 - Mentions: markdown renderer now wraps username in hovercard logic allowing checking the mentioned user.
20 - Documentation: added note about hard restart due to celery update.
21 - Maintenance: run rebuildfncache for Mercurial in maintenance command.
22
23
24 Security
25 ^^^^^^^^
26
27
28
29 Performance
30 ^^^^^^^^^^^
31
32 - Authentication: cache plugins for auth and their settings in the auth_registry for single request.
33 This heavily influences SVN performance on multiple-file commits.
34
35
36 Fixes
37 ^^^^^
38
39 - Descriptions: fixed rendering problem with certain meta-tags in repo description.
40 - Emails: fixed fonts rendering problems in Outlook.
41 - Emails: fixed bug in test email sending.
42 - Summary: fixed styling of readme indicator.
43 - Flash: fixed display problem with flash messages on error pages.
44
45
46 Upgrade notes
47 ^^^^^^^^^^^^^
48
49 - Scheduled release addressing problems in 4.18.X releases.
@@ -0,0 +1,64 b''
1 |RCE| 4.18.3 |RNS|
2 ------------------
3
4 Release Date
5 ^^^^^^^^^^^^
6
7 - 2020-03-24
8
9
10 New Features
11 ^^^^^^^^^^^^
12
13 - LDAP: added nested user groups sync which was planned in 4.18.X but didn't
14 make it to the release. New option for sync is available in the LDAP configuration.
15
16
17 General
18 ^^^^^^^
19
20 - API: added branch permissions functions.
21 - Pull requests: added creating indicator to let users know they should wait until PR is creating.
22 - Pull requests: allow super-admins to force change state of locked PRs.
23 - Users/User groups: in edit mode we now show the actual name of what we're editing.
24 - SSH: allow generation of legacy SSH keys for older systems and Windows users.
25 - File store: don't response with cookie data on file-store download response.
26 - File store: use our own logic for setting content-type. This solves a problem
27 when previously used resolver set different content-type+content-encoding which
28 is an incorrect behaviour.
29 - My Account: show info about password usage for external accounts e.g github/google etc
30 We now recommend using auth-tokens instead of actual passwords.
31 - Repositories: in description field we now show mention of metatags only if they
32 are enabled.
33
34
35 Security
36 ^^^^^^^^
37
38 - Remote sync: don't expose credentials in displayed URLs.
39 Remote links url had visible credentials displayed in the link.
40 This was used for web-view and not needed anymore.
41
42
43 Performance
44 ^^^^^^^^^^^
45
46 - Full text search: significantly improved GIT commit indexing performance by reducing
47 number of calls to the vcsserver.
48
49
50 Fixes
51 ^^^^^
52
53 - Mercurial: fixed cases of lookup of branches that are exactly 20 character long.
54 - SVN: allow legacy (pre SVN 1.7) extraction of post commit data.
55 - GIT: use non-unicode author extraction as it's returned as bytes from backend, and
56 we can get an unicode errors while there's some non-ascii characters.
57 - GIT: use safe configparser for git submodules to prevent from errors on submodules with % sign.
58 - System info: fixed UI problem with new version update info screen.
59
60
61 Upgrade notes
62 ^^^^^^^^^^^^^
63
64 - Scheduled release addressing problems in 4.18.X releases.
@@ -58,3 +58,7 b' ad5bd0c4bd322fdbd04bb825a3d027e08f7a3901'
58 58 037f5794b55a6236d68f6485a485372dde6566e0 v4.17.3
59 59 83bc3100cfd6094c1d04f475ddb299b7dc3d0b33 v4.17.4
60 60 e3de8c95baf8cc9109ca56aee8193a2cb6a54c8a v4.17.4
61 f37a3126570477543507f0bc9d245ce75546181a v4.18.0
62 71d8791463e87b64c1a18475de330ee600d37561 v4.18.1
63 4bd6b75dac1d25c64885d4d49385e5533f21c525 v4.18.2
64 12ed92fe57f2e9fc7b71dc0b65e26c2da5c7085f v4.18.3
@@ -57,5 +57,5 b' Each lines should represent a single nam'
57 57 Run this line from CLI to execute the code from the `repo_delete_task.py` file and
58 58 exit the ishell after the execution::
59 59
60 echo "%run repo_delete_task.py" | rccontrol ishell Enterprise-1
60 echo "%run repo_delete_task.py" | rccontrol ishell enterprise-1
61 61
@@ -124,6 +124,7 b' 1. To configure Apache, create and edit '
124 124 LogLevel info
125 125 # allows custom host names, prevents 400 errors on checkout
126 126 HttpProtocolOptions Unsafe
127 # Most likely this will be: /home/user/.rccontrol/enterprise-1/mod_dav_svn.conf
127 128 Include /home/user/.rccontrol/enterprise-1/mod_dav_svn.conf
128 129 </VirtualHost>
129 130
@@ -48,7 +48,7 b' uses, or if required it can be a differe'
48 48
49 49
50 50
51 To switch to reds-based user sessions uncomment the following section in
51 To switch to redis-based user sessions uncomment the following section in
52 52 your :file:`/home/{user}/.rccontrol/{instance-id}/rhodecode.ini` file.
53 53
54 54 .. code-block:: ini
@@ -33,6 +33,15 b' 2. Run the |RCC| installer and accept th'
33 33 Do you accept the RhodeCode Control license?
34 34 Press [Y] to accept license and [V] to view license text: y
35 35
36
37 .. important::
38
39 We recommend running RhodeCode as a non-root user, such as `rhodecode`;
40 this user must have a proper home directory.
41 Either log in as that user to install the software, or do it as root
42 with `sudo -i -u rhodecode ./RhodeCode-installer-linux-*`
43
44
36 45 3. Install a VCS Server, and configure it to start at boot.
37 46
38 47 .. code-block:: bash
@@ -193,6 +193,10 b' Fixes'
193 193 Upgrade notes
194 194 ^^^^^^^^^^^^^
195 195
196 - Major Celery Version upgrade. The 4.18.X release includes a major Celery version.
197 It's recommended to run `rccontrol self-stop && rccontrol self-init` after the
198 upgrade to ensure celery workers are restarted and updated.
199
196 200 - New Automation task. We've changed the logic for updating latest change inside repository group.
197 201 New logic includes scanning for changes in all nested objects. Since this is a heavy task
198 202 a new dedicated scheduler task has been created to update it automatically on a scheduled base.
@@ -218,6 +222,11 b' Upgrade notes'
218 222 Please review vcsserver.ini settings under:
219 223 `rc_cache.repo_object.backend = dogpile.cache.rc.redis_msgpack`
220 224
225 - Gunicorn configuration now moved to .ini files.
226 Upgrading to 4.18.X will overwrite the gunicorn_conf.py file. If there are any custom changes in that file
227 they will be lost. Recommended way to configure gunicorn is now via the .ini files. Please check `rhodecode.template.ini` file
228 for example gunicorn configuration.
229
221 230 - New memory monitoring for Gunicorn workers. Starting from 4.18 release a option was added
222 231 to limit the maximum amount of memory used by a worker.
223 232 Please review new settings in `[server:main]` section for memory management in both
@@ -9,6 +9,9 b' Release Notes'
9 9 .. toctree::
10 10 :maxdepth: 1
11 11
12 release-notes-4.18.3.rst
13 release-notes-4.18.2.rst
14 release-notes-4.18.1.rst
12 15 release-notes-4.18.0.rst
13 16 release-notes-4.17.4.rst
14 17 release-notes-4.17.3.rst
@@ -18,7 +18,6 b''
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 import inspect
22 21 import itertools
23 22 import logging
24 23 import sys
@@ -186,10 +185,12 b' def request_view(request):'
186 185 Main request handling method. It handles all logic to call a specific
187 186 exposed method
188 187 """
188 # cython compatible inspect
189 from rhodecode.config.patches import inspect_getargspec
190 inspect = inspect_getargspec()
189 191
190 192 # check if we can find this session using api_key, get_by_auth_token
191 193 # search not expired tokens only
192
193 194 try:
194 195 api_user = User.get_by_auth_token(request.rpc_api_key)
195 196
@@ -56,5 +56,6 b' class TestGetMethod(object):'
56 56 'request': '<RequiredType>',
57 57 'resolves_comment_id': '<Optional:None>',
58 58 'status': '<Optional:None>',
59 'userid': '<Optional:<OptionalAttr:apiuser>>'}]
59 'userid': '<Optional:<OptionalAttr:apiuser>>',
60 'send_email': '<Optional:True>'}]
60 61 assert_ok(id_, expected, given=response.body)
@@ -66,7 +66,7 b' class TestGrantUserGroupPermission(objec'
66 66 perm=perm)
67 67 response = api_call(self.app, params)
68 68
69 expected = 'permission `%s` does not exist' % (perm,)
69 expected = 'permission `%s` does not exist.' % (perm,)
70 70 assert_error(id_, expected, given=response.body)
71 71
72 72 @mock.patch.object(RepoModel, 'grant_user_group_permission', crash)
@@ -132,8 +132,7 b' class TestGrantUserGroupPermissionFromRe'
132 132 RepoGroupModel().revoke_user_group_permission(
133 133 repo_group.group_id, user_group.users_group_id)
134 134 else:
135 expected = 'repository group `%s` does not exist' % (
136 repo_group.name,)
135 expected = 'repository group `%s` does not exist' % (repo_group.name,)
137 136 assert_error(id_, expected, given=response.body)
138 137
139 138 def test_api_grant_user_group_permission_to_repo_group_wrong_permission(
@@ -149,7 +148,7 b' class TestGrantUserGroupPermissionFromRe'
149 148 perm=perm)
150 149 response = api_call(self.app, params)
151 150
152 expected = 'permission `%s` does not exist' % (perm,)
151 expected = 'permission `%s` does not exist. Permission should start with prefix: `group.`' % (perm,)
153 152 assert_error(id_, expected, given=response.body)
154 153
155 154 @mock.patch.object(RepoGroupModel, 'grant_user_group_permission', crash)
@@ -65,7 +65,7 b' class TestGrantUserPermission(object):'
65 65 perm=perm)
66 66 response = api_call(self.app, params)
67 67
68 expected = 'permission `%s` does not exist' % (perm,)
68 expected = 'permission `%s` does not exist.' % (perm,)
69 69 assert_error(id_, expected, given=response.body)
70 70
71 71 @mock.patch.object(RepoModel, 'grant_user_permission', crash)
@@ -132,7 +132,7 b' class TestGrantUserPermissionFromRepoGro'
132 132 perm=perm)
133 133 response = api_call(self.app, params)
134 134
135 expected = 'permission `%s` does not exist' % (perm,)
135 expected = 'permission `%s` does not exist. Permission should start with prefix: `group.`' % (perm,)
136 136 assert_error(id_, expected, given=response.body)
137 137
138 138 @mock.patch.object(RepoGroupModel, 'grant_user_permission', crash)
@@ -130,7 +130,7 b' class TestGrantUserPermissionFromUserGro'
130 130 perm=perm)
131 131 response = api_call(self.app, params)
132 132
133 expected = 'permission `%s` does not exist' % perm
133 expected = 'permission `%s` does not exist. Permission should start with prefix: `usergroup.`' % perm
134 134 assert_error(id_, expected, given=response.body)
135 135
136 136 def test_api_grant_user_permission_to_user_group_exception_when_adding(
@@ -308,7 +308,11 b' def get_perm_or_error(permid, prefix=Non'
308 308
309 309 perm = PermissionModel.cls.get_by_key(permid)
310 310 if perm is None:
311 raise JSONRPCError('permission `%s` does not exist' % (permid,))
311 msg = 'permission `{}` does not exist.'.format(permid)
312 if prefix:
313 msg += ' Permission should start with prefix: `{}`'.format(prefix)
314 raise JSONRPCError(msg)
315
312 316 if prefix:
313 317 if not perm.permission_name.startswith(prefix):
314 318 raise JSONRPCError('permission `%s` is invalid, '
@@ -351,12 +355,12 b' def get_pull_request_or_error(pullreques'
351 355 def build_commit_data(commit, detail_level):
352 356 parsed_diff = []
353 357 if detail_level == 'extended':
354 for f in commit.added:
355 parsed_diff.append(_get_commit_dict(filename=f.path, op='A'))
356 for f in commit.changed:
357 parsed_diff.append(_get_commit_dict(filename=f.path, op='M'))
358 for f in commit.removed:
359 parsed_diff.append(_get_commit_dict(filename=f.path, op='D'))
358 for f_path in commit.added_paths:
359 parsed_diff.append(_get_commit_dict(filename=f_path, op='A'))
360 for f_path in commit.changed_paths:
361 parsed_diff.append(_get_commit_dict(filename=f_path, op='M'))
362 for f_path in commit.removed_paths:
363 parsed_diff.append(_get_commit_dict(filename=f_path, op='D'))
360 364
361 365 elif detail_level == 'full':
362 366 from rhodecode.lib.diffs import DiffProcessor
@@ -73,6 +73,7 b' def get_pull_request(request, apiuser, p'
73 73 "status" : "<status>",
74 74 "created_on": "<date_time_created>",
75 75 "updated_on": "<date_time_updated>",
76 "versions": "<number_or_versions_of_pr>",
76 77 "commit_ids": [
77 78 ...
78 79 "<commit_id>",
@@ -452,7 +453,7 b' def comment_pull_request('
452 453 message=Optional(None), commit_id=Optional(None), status=Optional(None),
453 454 comment_type=Optional(ChangesetComment.COMMENT_TYPE_NOTE),
454 455 resolves_comment_id=Optional(None), extra_recipients=Optional([]),
455 userid=Optional(OAttr('apiuser'))):
456 userid=Optional(OAttr('apiuser')), send_email=Optional(True)):
456 457 """
457 458 Comment on the pull request specified with the `pullrequestid`,
458 459 in the |repo| specified by the `repoid`, and optionally change the
@@ -483,6 +484,8 b' def comment_pull_request('
483 484 :type extra_recipients: Optional(list)
484 485 :param userid: Comment on the pull request as this user
485 486 :type userid: Optional(str or int)
487 :param send_email: Define if this comment should also send email notification
488 :type send_email: Optional(bool)
486 489
487 490 Example output:
488 491
@@ -527,6 +530,7 b' def comment_pull_request('
527 530 comment_type = Optional.extract(comment_type)
528 531 resolves_comment_id = Optional.extract(resolves_comment_id)
529 532 extra_recipients = Optional.extract(extra_recipients)
533 send_email = Optional.extract(send_email, binary=True)
530 534
531 535 if not message and not status:
532 536 raise JSONRPCError(
@@ -587,7 +591,8 b' def comment_pull_request('
587 591 comment_type=comment_type,
588 592 resolves_comment_id=resolves_comment_id,
589 593 auth_user=auth_user,
590 extra_recipients=extra_recipients
594 extra_recipients=extra_recipients,
595 send_email=send_email
591 596 )
592 597
593 598 if allowed_to_change_status and status:
@@ -1551,7 +1551,7 b' def comment_commit('
1551 1551 request, apiuser, repoid, commit_id, message, status=Optional(None),
1552 1552 comment_type=Optional(ChangesetComment.COMMENT_TYPE_NOTE),
1553 1553 resolves_comment_id=Optional(None), extra_recipients=Optional([]),
1554 userid=Optional(OAttr('apiuser'))):
1554 userid=Optional(OAttr('apiuser')), send_email=Optional(True)):
1555 1555 """
1556 1556 Set a commit comment, and optionally change the status of the commit.
1557 1557
@@ -1575,6 +1575,8 b' def comment_commit('
1575 1575 :type extra_recipients: Optional(list)
1576 1576 :param userid: Set the user name of the comment creator.
1577 1577 :type userid: Optional(str or int)
1578 :param send_email: Define if this comment should also send email notification
1579 :type send_email: Optional(bool)
1578 1580
1579 1581 Example error output:
1580 1582
@@ -1610,6 +1612,7 b' def comment_commit('
1610 1612 comment_type = Optional.extract(comment_type)
1611 1613 resolves_comment_id = Optional.extract(resolves_comment_id)
1612 1614 extra_recipients = Optional.extract(extra_recipients)
1615 send_email = Optional.extract(send_email, binary=True)
1613 1616
1614 1617 allowed_statuses = [x[0] for x in ChangesetStatus.STATUSES]
1615 1618 if status and status not in allowed_statuses:
@@ -1639,7 +1642,8 b' def comment_commit('
1639 1642 comment_type=comment_type,
1640 1643 resolves_comment_id=resolves_comment_id,
1641 1644 auth_user=apiuser,
1642 extra_recipients=extra_recipients
1645 extra_recipients=extra_recipients,
1646 send_email=send_email
1643 1647 )
1644 1648 if status:
1645 1649 # also do a status change
@@ -18,7 +18,6 b''
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 import inspect
22 21 import logging
23 22 import itertools
24 23 import base64
@@ -334,6 +333,9 b' def get_method(request, apiuser, pattern'
334 333 ]
335 334 error : null
336 335 """
336 from rhodecode.config.patches import inspect_getargspec
337 inspect = inspect_getargspec()
338
337 339 if not has_superadmin_permission(apiuser):
338 340 raise JSONRPCForbidden()
339 341
@@ -37,7 +37,7 b' from rhodecode.model import user_group'
37 37 from rhodecode.model import user
38 38 from rhodecode.model.db import User
39 39 from rhodecode.model.scm import ScmModel
40 from rhodecode.model.settings import VcsSettingsModel
40 from rhodecode.model.settings import VcsSettingsModel, IssueTrackerSettingsModel
41 41 from rhodecode.model.repo import ReadmeFinder
42 42
43 43 log = logging.getLogger(__name__)
@@ -226,6 +226,7 b' class RepoAppView(BaseAppView):'
226 226 self.db_repo_name = self.db_repo.repo_name
227 227 self.db_repo_pull_requests = ScmModel().get_pull_requests(self.db_repo)
228 228 self.db_repo_artifacts = ScmModel().get_artifacts(self.db_repo)
229 self.db_repo_patterns = IssueTrackerSettingsModel(repo=self.db_repo)
229 230
230 231 def _handle_missing_requirements(self, error):
231 232 log.error(
@@ -573,7 +573,7 b' class AdminSettingsView(BaseAppView):'
573 573
574 574 email_kwargs = {
575 575 'date': datetime.datetime.now(),
576 'user': c.rhodecode_user
576 'user': self._rhodecode_db_user
577 577 }
578 578
579 579 (subject, headers, email_body,
@@ -872,7 +872,10 b' class UsersView(UserAppView):'
872 872
873 873 c.active = 'ssh_keys_generate'
874 874 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
875 c.private, c.public = SshKeyModel().generate_keypair(comment=comment)
875 private_format = self.request.GET.get('private_format') \
876 or SshKeyModel.DEFAULT_PRIVATE_KEY_FORMAT
877 c.private, c.public = SshKeyModel().generate_keypair(
878 comment=comment, private_format=private_format)
876 879
877 880 return self._get_template_context(c)
878 881
@@ -33,6 +33,7 b' from rhodecode.lib import audit_logger'
33 33 from rhodecode.lib.auth import (
34 34 CSRFRequired, NotAnonymous, HasRepoPermissionAny, HasRepoGroupPermissionAny,
35 35 LoginRequired)
36 from rhodecode.lib.vcs.conf.mtypes import get_mimetypes_db
36 37 from rhodecode.model.db import Session, FileStore, UserApiKeys
37 38
38 39 log = logging.getLogger(__name__)
@@ -46,6 +47,15 b' class FileStoreView(BaseAppView):'
46 47 self.storage = utils.get_file_storage(self.request.registry.settings)
47 48 return c
48 49
50 def _guess_type(self, file_name):
51 """
52 Our own type guesser for mimetypes using the rich DB
53 """
54 if not hasattr(self, 'db'):
55 self.db = get_mimetypes_db()
56 _content_type, _encoding = self.db.guess_type(file_name, strict=False)
57 return _content_type, _encoding
58
49 59 def _serve_file(self, file_uid):
50 60
51 61 if not self.storage.exists(file_uid):
@@ -92,7 +102,18 b' class FileStoreView(BaseAppView):'
92 102 FileStore.bump_access_counter(file_uid)
93 103
94 104 file_path = self.storage.store_path(file_uid)
95 return FileResponse(file_path)
105 content_type = 'application/octet-stream'
106 content_encoding = None
107
108 _content_type, _encoding = self._guess_type(file_path)
109 if _content_type:
110 content_type = _content_type
111
112 # For file store we don't submit any session data, this logic tells the
113 # Session lib to skip it
114 setattr(self.request, '_file_response', True)
115 return FileResponse(file_path, request=self.request,
116 content_type=content_type, content_encoding=content_encoding)
96 117
97 118 @LoginRequired()
98 119 @NotAnonymous()
@@ -105,11 +105,11 b' class TestGistsController(TestController'
105 105 g4 = create_gist('gist4', gist_type='private').gist_access_id
106 106 response = self.app.get(route_path('gists_show'))
107 107
108 response.mustcontain('gist: %s' % g1.gist_access_id)
109 response.mustcontain('gist: %s' % g2.gist_access_id)
110 response.mustcontain('gist: %s' % g3.gist_access_id)
108 response.mustcontain(g1.gist_access_id)
109 response.mustcontain(g2.gist_access_id)
110 response.mustcontain(g3.gist_access_id)
111 111 response.mustcontain('gist3-desc')
112 response.mustcontain(no=['gist: %s' % g4])
112 response.mustcontain(no=[g4])
113 113
114 114 # Expiration information should be visible
115 115 expires_tag = '%s' % h.age_component(
@@ -122,7 +122,7 b' class TestGistsController(TestController'
122 122 response = self.app.get(route_path('gists_show', params=dict(private=1)))
123 123
124 124 # and privates
125 response.mustcontain('gist: %s' % gist.gist_access_id)
125 response.mustcontain(gist.gist_access_id)
126 126
127 127 def test_index_show_all(self, create_gist):
128 128 self.log_user()
@@ -136,7 +136,7 b' class TestGistsController(TestController'
136 136 assert len(GistModel.get_all()) == 4
137 137 # and privates
138 138 for gist in GistModel.get_all():
139 response.mustcontain('gist: %s' % gist.gist_access_id)
139 response.mustcontain(gist.gist_access_id)
140 140
141 141 def test_index_show_all_hidden_from_regular(self, create_gist):
142 142 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
@@ -150,7 +150,7 b' class TestGistsController(TestController'
150 150 # since we don't have access to private in this view, we
151 151 # should see nothing
152 152 for gist in GistModel.get_all():
153 response.mustcontain(no=['gist: %s' % gist.gist_access_id])
153 response.mustcontain(no=[gist.gist_access_id])
154 154
155 155 def test_create(self):
156 156 self.log_user()
@@ -33,7 +33,7 b' def assert_and_get_main_filter_content(r'
33 33 if data_item['type'] == 'search':
34 34 display_val = data_item['value_display']
35 35 if data_item['id'] == -1:
36 assert 'File search for:' in display_val, display_val
36 assert 'File content search for:' in display_val, display_val
37 37 elif data_item['id'] == -2:
38 38 assert 'Commit search for:' in display_val, display_val
39 39 else:
@@ -453,7 +453,7 b' class HomeView(BaseAppView, DataGridAppV'
453 453 qry = query
454 454 return {'q': qry, 'type': 'content'}
455 455
456 label = u'File search for `{}`'.format(h.escape(query))
456 label = u'File content search for `{}`'.format(h.escape(query))
457 457 file_qry = {
458 458 'id': -10,
459 459 'value': query,
@@ -497,7 +497,7 b' class HomeView(BaseAppView, DataGridAppV'
497 497 qry = query
498 498 return {'q': qry, 'type': 'content'}
499 499
500 label = u'File search for `{}`'.format(query)
500 label = u'File content search for `{}`'.format(query)
501 501 file_qry = {
502 502 'id': -30,
503 503 'value': query,
@@ -541,7 +541,7 b' class HomeView(BaseAppView, DataGridAppV'
541 541 {
542 542 'id': -1,
543 543 'value': query,
544 'value_display': u'File search for: `{}`'.format(query),
544 'value_display': u'File content search for: `{}`'.format(query),
545 545 'value_icon': '<i class="icon-code"></i>',
546 546 'type': 'search',
547 547 'subtype': 'global',
@@ -26,6 +26,10 b' def includeme(config):'
26 26 pattern='/_hovercard/user/{user_id}')
27 27
28 28 config.add_route(
29 name='hovercard_username',
30 pattern='/_hovercard/username/{username}')
31
32 config.add_route(
29 33 name='hovercard_user_group',
30 34 pattern='/_hovercard/user_group/{user_group_id}')
31 35
@@ -65,6 +65,19 b' class HoverCardsView(BaseAppView):'
65 65
66 66 @LoginRequired()
67 67 @view_config(
68 route_name='hovercard_username', request_method='GET', xhr=True,
69 renderer='rhodecode:templates/hovercards/hovercard_user.mako')
70 def hovercard_username(self):
71 c = self.load_default_context()
72 username = self.request.matchdict['username']
73 c.user = User.get_by_username(username)
74 if not c.user:
75 raise HTTPNotFound()
76
77 return self._get_template_context(c)
78
79 @LoginRequired()
80 @view_config(
68 81 route_name='hovercard_user_group', request_method='GET', xhr=True,
69 82 renderer='rhodecode:templates/hovercards/hovercard_user_group.mako')
70 83 def hovercard_user_group(self):
@@ -108,7 +108,8 b' class TestLoginController(object):'
108 108
109 109 def test_login_regular_forbidden_when_super_admin_restriction(self):
110 110 from rhodecode.authentication.plugins.auth_rhodecode import RhodeCodeAuthPlugin
111 with fixture.auth_restriction(RhodeCodeAuthPlugin.AUTH_RESTRICTION_SUPER_ADMIN):
111 with fixture.auth_restriction(self.app._pyramid_registry,
112 RhodeCodeAuthPlugin.AUTH_RESTRICTION_SUPER_ADMIN):
112 113 response = self.app.post(route_path('login'),
113 114 {'username': 'test_regular',
114 115 'password': 'test12'})
@@ -118,7 +119,8 b' class TestLoginController(object):'
118 119
119 120 def test_login_regular_forbidden_when_scope_restriction(self):
120 121 from rhodecode.authentication.plugins.auth_rhodecode import RhodeCodeAuthPlugin
121 with fixture.scope_restriction(RhodeCodeAuthPlugin.AUTH_RESTRICTION_SCOPE_VCS):
122 with fixture.scope_restriction(self.app._pyramid_registry,
123 RhodeCodeAuthPlugin.AUTH_RESTRICTION_SCOPE_VCS):
122 124 response = self.app.post(route_path('login'),
123 125 {'username': 'test_regular',
124 126 'password': 'test12'})
@@ -76,6 +76,7 b' class MyAccountView(BaseAppView, DataGri'
76 76 def my_account_profile(self):
77 77 c = self.load_default_context()
78 78 c.active = 'profile'
79 c.extern_type = c.user.extern_type
79 80 return self._get_template_context(c)
80 81
81 82 @LoginRequired()
@@ -72,8 +72,11 b' class MyAccountSshKeysView(BaseAppView, '
72 72
73 73 c.active = 'ssh_keys_generate'
74 74 if c.ssh_key_generator_enabled:
75 private_format = self.request.GET.get('private_format') \
76 or SshKeyModel.DEFAULT_PRIVATE_KEY_FORMAT
75 77 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
76 c.private, c.public = SshKeyModel().generate_keypair(comment=comment)
78 c.private, c.public = SshKeyModel().generate_keypair(
79 comment=comment, private_format=private_format)
77 80 c.target_form_url = h.route_path(
78 81 'my_account_ssh_keys', _query=dict(default_key=c.public))
79 82 return self._get_template_context(c)
@@ -28,6 +28,7 b' from rhodecode.lib import helpers as h'
28 28 from rhodecode.lib import audit_logger
29 29 from rhodecode.lib.auth import (
30 30 LoginRequired, HasRepoGroupPermissionAnyDecorator, CSRFRequired)
31 from rhodecode.model.db import User
31 32 from rhodecode.model.permission import PermissionModel
32 33 from rhodecode.model.repo_group import RepoGroupModel
33 34 from rhodecode.model.forms import RepoGroupPermsForm
@@ -96,7 +97,13 b' class RepoGroupPermissionsView(RepoGroup'
96 97
97 98 Session().commit()
98 99 h.flash(_('Repository Group permissions updated'), category='success')
99 PermissionModel().flush_user_permission_caches(changes)
100
101 affected_user_ids = None
102 if changes.get('default_user_changed', False):
103 # if we change the default user, we need to flush everyone permissions
104 affected_user_ids = User.get_all_user_ids()
105 PermissionModel().flush_user_permission_caches(
106 changes, affected_user_ids=affected_user_ids)
100 107
101 108 raise HTTPFound(
102 109 h.route_path('edit_repo_group_perms',
@@ -28,6 +28,8 b' from rhodecode.lib import helpers as h'
28 28 from rhodecode.lib import audit_logger
29 29 from rhodecode.lib.auth import (
30 30 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
31 from rhodecode.lib.utils2 import str2bool
32 from rhodecode.model.db import User
31 33 from rhodecode.model.forms import RepoPermsForm
32 34 from rhodecode.model.meta import Session
33 35 from rhodecode.model.permission import PermissionModel
@@ -89,7 +91,12 b' class RepoSettingsPermissionsView(RepoAp'
89 91 Session().commit()
90 92 h.flash(_('Repository access permissions updated'), category='success')
91 93
92 PermissionModel().flush_user_permission_caches(changes)
94 affected_user_ids = None
95 if changes.get('default_user_changed', False):
96 # if we change the default user, we need to flush everyone permissions
97 affected_user_ids = User.get_all_user_ids()
98 PermissionModel().flush_user_permission_caches(
99 changes, affected_user_ids=affected_user_ids)
93 100
94 101 raise HTTPFound(
95 102 h.route_path('edit_repo_perms', repo_name=self.db_repo_name))
@@ -104,9 +111,11 b' class RepoSettingsPermissionsView(RepoAp'
104 111 _ = self.request.translate
105 112 self.load_default_context()
106 113
114 private_flag = str2bool(self.request.POST.get('private'))
115
107 116 try:
108 117 RepoModel().update(
109 self.db_repo, **{'repo_private': True, 'repo_name': self.db_repo_name})
118 self.db_repo, **{'repo_private': private_flag, 'repo_name': self.db_repo_name})
110 119 Session().commit()
111 120
112 121 h.flash(_('Repository `{}` private mode set successfully').format(self.db_repo_name),
@@ -116,7 +125,11 b' class RepoSettingsPermissionsView(RepoAp'
116 125 h.flash(_('Error occurred during update of repository {}').format(
117 126 self.db_repo_name), category='error')
118 127
128 # NOTE(dan): we change repo private mode we need to notify all USERS
129 affected_user_ids = User.get_all_user_ids()
130 PermissionModel().trigger_permission_flush(affected_user_ids)
131
119 132 return {
120 133 'redirect_url': h.route_path('edit_repo_perms', repo_name=self.db_repo_name),
121 'private': True
134 'private': private_flag
122 135 }
@@ -275,6 +275,20 b' class RepoPullRequestsView(RepoAppView, '
275 275
276 276 c.state_progressing = pull_request.is_state_changing()
277 277
278 _new_state = {
279 'created': PullRequest.STATE_CREATED,
280 }.get(self.request.GET.get('force_state'))
281 if c.is_super_admin and _new_state:
282 with pull_request.set_state(PullRequest.STATE_UPDATING, final_state=_new_state):
283 h.flash(
284 _('Pull Request state was force changed to `{}`').format(_new_state),
285 category='success')
286 Session().commit()
287
288 raise HTTPFound(h.route_path(
289 'pullrequest_show', repo_name=self.db_repo_name,
290 pull_request_id=pull_request_id))
291
278 292 version = self.request.GET.get('version')
279 293 from_version = self.request.GET.get('from_version') or version
280 294 merge_checks = self.request.GET.get('merge_checks')
@@ -31,7 +31,7 b' from rhodecode.lib.auth import ('
31 31 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
32 32 from rhodecode.model.forms import IssueTrackerPatternsForm
33 33 from rhodecode.model.meta import Session
34 from rhodecode.model.settings import IssueTrackerSettingsModel, SettingsModel
34 from rhodecode.model.settings import SettingsModel
35 35
36 36 log = logging.getLogger(__name__)
37 37
@@ -53,7 +53,7 b' class RepoSettingsIssueTrackersView(Repo'
53 53 c.active = 'issuetracker'
54 54 c.data = 'data'
55 55
56 c.settings_model = IssueTrackerSettingsModel(repo=self.db_repo)
56 c.settings_model = self.db_repo_patterns
57 57 c.global_patterns = c.settings_model.get_global_settings()
58 58 c.repo_patterns = c.settings_model.get_repo_settings()
59 59
@@ -79,7 +79,7 b' class RepoSettingsIssueTrackersView(Repo'
79 79 def repo_issuetracker_delete(self):
80 80 _ = self.request.translate
81 81 uid = self.request.POST.get('uid')
82 repo_settings = IssueTrackerSettingsModel(repo=self.db_repo_name)
82 repo_settings = self.db_repo_patterns
83 83 try:
84 84 repo_settings.delete_entries(uid)
85 85 except Exception:
@@ -113,7 +113,7 b' class RepoSettingsIssueTrackersView(Repo'
113 113 def repo_issuetracker_update(self):
114 114 _ = self.request.translate
115 115 # Save inheritance
116 repo_settings = IssueTrackerSettingsModel(repo=self.db_repo_name)
116 repo_settings = self.db_repo_patterns
117 117 inherited = (
118 118 self.request.POST.get('inherit_global_issuetracker') == "inherited")
119 119 repo_settings.inherit_global_settings = inherited
@@ -24,6 +24,7 b''
24 24 # LogLevel info
25 25 # # allows custom host names, prevents 400 errors on checkout
26 26 # HttpProtocolOptions Unsafe
27 # # Most likely this will be: /home/user/.rccontrol/enterprise-1/mod_dav_svn.conf
27 28 # Include /path/to/generated/mod_dav_svn.conf
28 29 # </VirtualHost>
29 30 #
@@ -150,6 +150,7 b' class RhodeCodeAuthPluginBase(object):'
150 150
151 151 def __init__(self, plugin_id):
152 152 self._plugin_id = plugin_id
153 self._settings = {}
153 154
154 155 def __str__(self):
155 156 return self.get_id()
@@ -226,17 +227,26 b' class RhodeCodeAuthPluginBase(object):'
226 227 """
227 228 return AuthnPluginSettingsSchemaBase()
228 229
229 def get_settings(self):
230 """
231 Returns the plugin settings as dictionary.
232 """
230 def _propagate_settings(self, raw_settings):
233 231 settings = {}
234 raw_settings = SettingsModel().get_all_settings()
235 232 for node in self.get_settings_schema():
236 233 settings[node.name] = self.get_setting_by_name(
237 234 node.name, plugin_cached_settings=raw_settings)
238 235 return settings
239 236
237 def get_settings(self, use_cache=True):
238 """
239 Returns the plugin settings as dictionary.
240 """
241 if self._settings != {} and use_cache:
242 return self._settings
243
244 raw_settings = SettingsModel().get_all_settings()
245 settings = self._propagate_settings(raw_settings)
246
247 self._settings = settings
248 return self._settings
249
240 250 def get_setting_by_name(self, name, default=None, plugin_cached_settings=None):
241 251 """
242 252 Returns a plugin setting by name.
@@ -594,19 +604,19 b' class AuthLdapBase(object):'
594 604 if not full_resolve:
595 605 return '{}:{}'.format(host, port)
596 606
597 log.debug('LDAP: Resolving IP for LDAP host %s', host)
607 log.debug('LDAP: Resolving IP for LDAP host `%s`', host)
598 608 try:
599 609 ip = socket.gethostbyname(host)
600 log.debug('Got LDAP server %s ip %s', host, ip)
610 log.debug('LDAP: Got LDAP host `%s` ip %s', host, ip)
601 611 except Exception:
602 raise LdapConnectionError(
603 'Failed to resolve host: `{}`'.format(host))
612 raise LdapConnectionError('Failed to resolve host: `{}`'.format(host))
604 613
605 614 log.debug('LDAP: Checking if IP %s is accessible', ip)
606 615 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
607 616 try:
608 617 s.connect((ip, int(port)))
609 618 s.shutdown(socket.SHUT_RD)
619 log.debug('LDAP: connection to %s successful', ip)
610 620 except Exception:
611 621 raise LdapConnectionError(
612 622 'Failed to connect to host: `{}:{}`'.format(host, port))
@@ -667,7 +677,7 b' def loadplugin(plugin_id):'
667 677
668 678 def get_authn_registry(registry=None):
669 679 registry = registry or get_current_registry()
670 authn_registry = registry.getUtility(IAuthnPluginRegistry)
680 authn_registry = registry.queryUtility(IAuthnPluginRegistry)
671 681 return authn_registry
672 682
673 683
@@ -690,6 +700,7 b' def authenticate(username, password, env'
690 700 headers_only = environ and not (username and password)
691 701
692 702 authn_registry = get_authn_registry(registry)
703
693 704 plugins_to_check = authn_registry.get_plugins_for_authentication()
694 705 log.debug('Starting ordered authentication chain using %s plugins',
695 706 [x.name for x in plugins_to_check])
@@ -145,16 +145,16 b' class AuthLdap(AuthLdapBase):'
145 145 log.debug('Trying simple_bind with password and given login DN: %r',
146 146 self.LDAP_BIND_DN)
147 147 ldap_conn.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS)
148
148 log.debug('simple_bind successful')
149 149 return ldap_conn
150 150
151 151 def fetch_attrs_from_simple_bind(self, server, dn, username, password):
152 152 try:
153 153 log.debug('Trying simple bind with %r', dn)
154 154 server.simple_bind_s(dn, safe_str(password))
155 user = server.search_ext_s(
155 _dn, attrs = server.search_ext_s(
156 156 dn, ldap.SCOPE_BASE, '(objectClass=*)', )[0]
157 _, attrs = user
157
158 158 return attrs
159 159
160 160 except ldap.INVALID_CREDENTIALS:
@@ -206,7 +206,7 b' class AuthLdap(AuthLdapBase):'
206 206 break
207 207 else:
208 208 raise LdapPasswordError(
209 'Failed to authenticate user `{}`'
209 'Failed to authenticate user `{}` '
210 210 'with given password'.format(username))
211 211
212 212 except ldap.NO_SUCH_OBJECT:
@@ -249,7 +249,7 b' class LdapSettingsSchema(AuthnPluginSett'
249 249 colander.Int(),
250 250 default=389,
251 251 description=_('Custom port that the LDAP server is listening on. '
252 'Default value is: 389, use 689 for LDAPS(SSL)'),
252 'Default value is: 389, use 689 for LDAPS (SSL)'),
253 253 preparer=strip_whitespace,
254 254 title=_('Port'),
255 255 validator=colander.Range(min=0, max=65536),
@@ -272,7 +272,7 b' class LdapSettingsSchema(AuthnPluginSett'
272 272 'uid=root,cn=users,dc=mydomain,dc=com, or admin@mydomain.com'),
273 273 missing='',
274 274 preparer=strip_whitespace,
275 title=_('Account'),
275 title=_('Bind account'),
276 276 widget='string')
277 277 dn_pass = colander.SchemaNode(
278 278 colander.String(),
@@ -280,7 +280,7 b' class LdapSettingsSchema(AuthnPluginSett'
280 280 description=_('Password to authenticate for given user DN.'),
281 281 missing='',
282 282 preparer=strip_whitespace,
283 title=_('Password'),
283 title=_('Bind account password'),
284 284 widget='password')
285 285 tls_kind = colander.SchemaNode(
286 286 colander.String(),
@@ -318,7 +318,7 b' class LdapSettingsSchema(AuthnPluginSett'
318 318 colander.String(),
319 319 default='',
320 320 description=_('Base DN to search. Dynamic bind is supported. Add `$login` marker '
321 'in it to be replaced with current user credentials \n'
321 'in it to be replaced with current user username \n'
322 322 '(e.g., dc=mydomain,dc=com, or ou=Users,dc=mydomain,dc=com)'),
323 323 missing='',
324 324 preparer=strip_whitespace,
@@ -38,6 +38,7 b' class AuthenticationPluginRegistry(objec'
38 38
39 39 def __init__(self, settings):
40 40 self._plugins = {}
41 self._plugins_for_auth = None
41 42 self._fallback_plugin = settings.get(self.fallback_plugin_key, None)
42 43
43 44 def add_authn_plugin(self, config, plugin):
@@ -63,6 +64,10 b' class AuthenticationPluginRegistry(objec'
63 64 if plugin.uid == plugin_uid:
64 65 return plugin
65 66
67 def invalidate_plugins_for_auth(self):
68 log.debug('Invalidating cached plugins for authentication')
69 self._plugins_for_auth = None
70
66 71 def get_plugins_for_authentication(self):
67 72 """
68 73 Returns a list of plugins which should be consulted when authenticating
@@ -70,6 +75,9 b' class AuthenticationPluginRegistry(objec'
70 75 Additionally it includes the fallback plugin from the INI file, if
71 76 `rhodecode.auth_plugin_fallback` is set to a plugin ID.
72 77 """
78 if self._plugins_for_auth is not None:
79 return self._plugins_for_auth
80
73 81 plugins = []
74 82
75 83 # Add all enabled and active plugins to the list. We iterate over the
@@ -80,6 +88,9 b' class AuthenticationPluginRegistry(objec'
80 88 plugin = self.get_plugin(plugin_id)
81 89 if plugin is not None and plugin.is_active(
82 90 plugin_cached_settings=raw_settings):
91
92 # inject settings into plugin, we can re-use the DB fetched settings here
93 plugin._settings = plugin._propagate_settings(raw_settings)
83 94 plugins.append(plugin)
84 95
85 96 # Add the fallback plugin from ini file.
@@ -89,6 +100,8 b' class AuthenticationPluginRegistry(objec'
89 100 self._fallback_plugin)
90 101 plugin = self.get_plugin(self._fallback_plugin)
91 102 if plugin is not None and plugin not in plugins:
103 plugin._settings = plugin._propagate_settings(raw_settings)
92 104 plugins.append(plugin)
93 105
94 return plugins
106 self._plugins_for_auth = plugins
107 return self._plugins_for_auth
@@ -99,11 +99,12 b' class AuthnPluginViewBase(BaseAppView):'
99 99 for name, value in valid_data.items():
100 100 self.plugin.create_or_update_setting(name, value)
101 101 Session().commit()
102 SettingsModel().invalidate_settings_cache()
102 103
103 104 # Display success message and redirect.
104 105 h.flash(_('Auth settings updated successfully.'), category='success')
105 redirect_to = self.request.resource_path(
106 self.context, route_name='auth_home')
106 redirect_to = self.request.resource_path(self.context, route_name='auth_home')
107
107 108 return HTTPFound(redirect_to)
108 109
109 110
@@ -159,7 +160,7 b' class AuthSettingsView(BaseAppView):'
159 160 'auth_plugins', plugins)
160 161 Session().add(setting)
161 162 Session().commit()
162
163 SettingsModel().invalidate_settings_cache()
163 164 h.flash(_('Auth settings updated successfully.'), category='success')
164 165 except formencode.Invalid as errors:
165 166 e = errors.error_dict or {}
@@ -174,6 +175,6 b' class AuthSettingsView(BaseAppView):'
174 175 h.flash(_('Error occurred during update of auth settings.'),
175 176 category='error')
176 177
177 redirect_to = self.request.resource_path(
178 self.context, route_name='auth_home')
178 redirect_to = self.request.resource_path(self.context, route_name='auth_home')
179
179 180 return HTTPFound(redirect_to)
@@ -95,3 +95,5 b' def inspect_getargspec():'
95 95 return inspect.ArgSpec(args, varargs, varkw, func.func_defaults)
96 96
97 97 inspect.getargspec = custom_getargspec
98
99 return inspect
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -151,20 +151,22 b' def _store_log(action_name, action_data,'
151 151
152 152
153 153 def store_web(*args, **kwargs):
154 if 'action_data' not in kwargs:
155 kwargs['action_data'] = {}
156 kwargs['action_data'].update({
157 'source': SOURCE_WEB
158 })
154 action_data = {}
155 org_action_data = kwargs.pop('action_data', {})
156 action_data.update(org_action_data)
157 action_data['source'] = SOURCE_WEB
158 kwargs['action_data'] = action_data
159
159 160 return store(*args, **kwargs)
160 161
161 162
162 163 def store_api(*args, **kwargs):
163 if 'action_data' not in kwargs:
164 kwargs['action_data'] = {}
165 kwargs['action_data'].update({
166 'source': SOURCE_API
167 })
164 action_data = {}
165 org_action_data = kwargs.pop('action_data', {})
166 action_data.update(org_action_data)
167 action_data['source'] = SOURCE_API
168 kwargs['action_data'] = action_data
169
168 170 return store(*args, **kwargs)
169 171
170 172
@@ -24,7 +24,6 b' authentication and permission libraries'
24 24
25 25 import os
26 26 import time
27 import inspect
28 27 import collections
29 28 import fnmatch
30 29 import hashlib
@@ -2013,6 +2012,7 b' class PermsFunction(object):'
2013 2012 self.user_group_name = None
2014 2013
2015 2014 def __bool__(self):
2015 import inspect
2016 2016 frame = inspect.currentframe()
2017 2017 stack_trace = traceback.format_stack(frame)
2018 2018 log.error('Checking bool value on a class instance of perm '
@@ -211,8 +211,9 b' def vcs_operation_context('
211 211 class BasicAuth(AuthBasicAuthenticator):
212 212
213 213 def __init__(self, realm, authfunc, registry, auth_http_code=None,
214 initial_call_detection=False, acl_repo_name=None):
214 initial_call_detection=False, acl_repo_name=None, rc_realm=''):
215 215 self.realm = realm
216 self.rc_realm = rc_realm
216 217 self.initial_call = initial_call_detection
217 218 self.authfunc = authfunc
218 219 self.registry = registry
@@ -227,7 +228,7 b' class BasicAuth(AuthBasicAuthenticator):'
227 228 return HTTPForbidden
228 229
229 230 def get_rc_realm(self):
230 return safe_str(self.registry.rhodecode_settings.get('rhodecode_realm'))
231 return safe_str(self.rc_realm)
231 232
232 233 def build_authentication(self):
233 234 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
@@ -288,7 +289,7 b' def attach_context_attributes(context, r'
288 289 """
289 290 config = request.registry.settings
290 291
291 rc_config = SettingsModel().get_all_settings(cache=True)
292 rc_config = SettingsModel().get_all_settings(cache=True, from_request=False)
292 293 context.rc_config = rc_config
293 294 context.rhodecode_version = rhodecode.__version__
294 295 context.rhodecode_edition = config.get('rhodecode.edition')
@@ -66,11 +66,12 b' markdown_tags = ['
66 66 markdown_attrs = {
67 67 "*": ["class", "style", "align"],
68 68 "img": ["src", "alt", "title"],
69 "a": ["href", "alt", "title", "name"],
69 "a": ["href", "alt", "title", "name", "data-hovercard-alt", "data-hovercard-url"],
70 70 "abbr": ["title"],
71 71 "acronym": ["title"],
72 72 "pre": ["lang"],
73 "input": ["type", "disabled", "checked"]
73 "input": ["type", "disabled", "checked"],
74 "strong": ["title", "data-hovercard-alt", "data-hovercard-url"],
74 75 }
75 76
76 77 standard_styles = [
@@ -421,9 +421,20 b' class DbManage(object):'
421 421
422 422 :param skip_existing:
423 423 """
424 defaults = [
425 ('auth_plugins',
426 'egg:rhodecode-enterprise-ce#token,egg:rhodecode-enterprise-ce#rhodecode',
427 'list'),
424 428
425 for k, v, t in [('auth_plugins', 'egg:rhodecode-enterprise-ce#rhodecode', 'list'),
426 ('auth_rhodecode_enabled', 'True', 'bool')]:
429 ('auth_authtoken_enabled',
430 'True',
431 'bool'),
432
433 ('auth_rhodecode_enabled',
434 'True',
435 'bool'),
436 ]
437 for k, v, t in defaults:
427 438 if (skip_existing and
428 439 SettingsModel().get_setting_by_name(k) is not None):
429 440 log.debug('Skipping option %s', k)
@@ -568,19 +579,32 b' class DbManage(object):'
568 579 ('title', '', 'unicode'),
569 580 ('pre_code', '', 'unicode'),
570 581 ('post_code', '', 'unicode'),
582
583 # Visual
571 584 ('show_public_icon', True, 'bool'),
572 585 ('show_private_icon', True, 'bool'),
573 586 ('stylify_metatags', False, 'bool'),
574 587 ('dashboard_items', 100, 'int'),
575 588 ('admin_grid_items', 25, 'int'),
589
590 ('markup_renderer', 'markdown', 'unicode'),
591
576 592 ('show_version', True, 'bool'),
593 ('show_revision_number', True, 'bool'),
594 ('show_sha_length', 12, 'int'),
595
577 596 ('use_gravatar', False, 'bool'),
578 597 ('gravatar_url', User.DEFAULT_GRAVATAR_URL, 'unicode'),
598
579 599 ('clone_uri_tmpl', Repository.DEFAULT_CLONE_URI, 'unicode'),
600 ('clone_uri_ssh_tmpl', Repository.DEFAULT_CLONE_URI_SSH, 'unicode'),
580 601 ('support_url', '', 'unicode'),
581 602 ('update_url', RhodeCodeSetting.DEFAULT_UPDATE_URL, 'unicode'),
582 ('show_revision_number', True, 'bool'),
583 ('show_sha_length', 12, 'int'),
603
604 # VCS Settings
605 ('pr_merge_enabled', True, 'bool'),
606 ('use_outdated_comments', True, 'bool'),
607 ('diff_cache', True, 'bool'),
584 608 ]
585 609
586 610 for key, val, type_ in settings:
@@ -598,9 +598,10 b' class _Message(object):'
598 598 * ``category``: the category specified when the message was created.
599 599 """
600 600
601 def __init__(self, category, message):
601 def __init__(self, category, message, sub_data=None):
602 602 self.category = category
603 603 self.message = message
604 self.sub_data = sub_data or {}
604 605
605 606 def __str__(self):
606 607 return self.message
@@ -663,7 +664,17 b' class Flash(object):'
663 664 # of strings.
664 665 for cat in self.categories:
665 666 for msg in session.pop_flash(queue=cat):
666 messages.append(_Message(cat, msg))
667 sub_data = {}
668 if hasattr(msg, 'rsplit'):
669 flash_data = msg.rsplit('|DELIM|', 1)
670 org_message = flash_data[0]
671 if len(flash_data) > 1:
672 sub_data = json.loads(flash_data[1])
673 else:
674 org_message = msg
675
676 messages.append(_Message(cat, org_message, sub_data=sub_data))
677
667 678 # Map messages from the default queue to the 'notice' category.
668 679 for msg in session.pop_flash():
669 680 messages.append(_Message('notice', msg))
@@ -673,25 +684,16 b' class Flash(object):'
673 684
674 685 def json_alerts(self, session=None, request=None):
675 686 payloads = []
676 messages = flash.pop_messages(session=session, request=request)
677 if messages:
678 for message in messages:
679 subdata = {}
680 if hasattr(message.message, 'rsplit'):
681 flash_data = message.message.rsplit('|DELIM|', 1)
682 org_message = flash_data[0]
683 if len(flash_data) > 1:
684 subdata = json.loads(flash_data[1])
685 else:
686 org_message = message.message
687 payloads.append({
688 'message': {
689 'message': u'{}'.format(org_message),
690 'level': message.category,
691 'force': True,
692 'subdata': subdata
693 }
694 })
687 messages = flash.pop_messages(session=session, request=request) or []
688 for message in messages:
689 payloads.append({
690 'message': {
691 'message': u'{}'.format(message.message),
692 'level': message.category,
693 'force': True,
694 'subdata': message.sub_data
695 }
696 })
695 697 return json.dumps(payloads)
696 698
697 699 def __call__(self, message, category=None, ignore_duplicate=True,
@@ -1514,6 +1516,9 b' def get_active_pattern_entries(repo_name'
1514 1516 return active_entries
1515 1517
1516 1518
1519 pr_pattern_re = re.compile(r'(?:(?:^!)|(?: !))(\d+)')
1520
1521
1517 1522 def process_patterns(text_string, repo_name, link_format='html', active_entries=None):
1518 1523
1519 1524 allowed_formats = ['html', 'rst', 'markdown',
@@ -1522,7 +1527,10 b' def process_patterns(text_string, repo_n'
1522 1527 raise ValueError('Link format can be only one of:{} got {}'.format(
1523 1528 allowed_formats, link_format))
1524 1529
1525 active_entries = active_entries or get_active_pattern_entries(repo_name)
1530 if active_entries is None:
1531 log.debug('Fetch active patterns for repo: %s', repo_name)
1532 active_entries = get_active_pattern_entries(repo_name)
1533
1526 1534 issues_data = []
1527 1535 new_text = text_string
1528 1536
@@ -1537,11 +1545,14 b' def process_patterns(text_string, repo_n'
1537 1545 log.debug('issue tracker entry: uid: `%s` PAT:%s URL:%s PREFIX:%s',
1538 1546 uid, entry['pat'], entry['url'], entry['pref'])
1539 1547
1540 try:
1541 pattern = re.compile(r'%s' % entry['pat'])
1542 except re.error:
1543 log.exception('issue tracker pattern: `%s` failed to compile', entry['pat'])
1544 continue
1548 if entry.get('pat_compiled'):
1549 pattern = entry['pat_compiled']
1550 else:
1551 try:
1552 pattern = re.compile(r'%s' % entry['pat'])
1553 except re.error:
1554 log.exception('issue tracker pattern: `%s` failed to compile', entry['pat'])
1555 continue
1545 1556
1546 1557 data_func = partial(
1547 1558 _process_url_func, repo_name=repo_name, entry=entry, uid=uid,
@@ -1569,7 +1580,7 b' def process_patterns(text_string, repo_n'
1569 1580 pr_url_func = partial(
1570 1581 _process_url_func, repo_name=repo_name, entry=pr_entry, uid=None,
1571 1582 link_format=link_format+'+hovercard')
1572 new_text = re.compile(r'(?:(?:^!)|(?: !))(\d+)').sub(pr_url_func, new_text)
1583 new_text = pr_pattern_re.sub(pr_url_func, new_text)
1573 1584 log.debug('processed !pr pattern')
1574 1585
1575 1586 return new_text, issues_data
@@ -1580,6 +1591,7 b' def urlify_commit_message(commit_text, r'
1580 1591 Parses given text message and makes proper links.
1581 1592 issues are linked to given issue-server, and rest is a commit link
1582 1593 """
1594
1583 1595 def escaper(_text):
1584 1596 return _text.replace('<', '&lt;').replace('>', '&gt;')
1585 1597
@@ -1636,7 +1648,7 b' def renderer_from_filename(filename, exc'
1636 1648
1637 1649
1638 1650 def render(source, renderer='rst', mentions=False, relative_urls=None,
1639 repo_name=None):
1651 repo_name=None, active_pattern_entries=None):
1640 1652
1641 1653 def maybe_convert_relative_links(html_source):
1642 1654 if relative_urls:
@@ -1651,7 +1663,8 b" def render(source, renderer='rst', menti"
1651 1663 if repo_name:
1652 1664 # process patterns on comments if we pass in repo name
1653 1665 source, issues = process_patterns(
1654 source, repo_name, link_format='rst')
1666 source, repo_name, link_format='rst',
1667 active_entries=active_pattern_entries)
1655 1668
1656 1669 return literal(
1657 1670 '<div class="rst-block">%s</div>' %
@@ -1662,7 +1675,8 b" def render(source, renderer='rst', menti"
1662 1675 if repo_name:
1663 1676 # process patterns on comments if we pass in repo name
1664 1677 source, issues = process_patterns(
1665 source, repo_name, link_format='markdown')
1678 source, repo_name, link_format='markdown',
1679 active_entries=active_pattern_entries)
1666 1680
1667 1681 return literal(
1668 1682 '<div class="markdown-block">%s</div>' %
@@ -47,6 +47,14 b' log = logging.getLogger(__name__)'
47 47 # default renderer used to generate automated comments
48 48 DEFAULT_COMMENTS_RENDERER = 'rst'
49 49
50 try:
51 from lxml.html import fromstring
52 from lxml.html import tostring
53 except ImportError:
54 log.exception('Failed to import lxml')
55 fromstring = None
56 tostring = None
57
50 58
51 59 class CustomHTMLTranslator(writers.html4css1.HTMLTranslator):
52 60 """
@@ -81,11 +89,7 b' def relative_links(html_source, server_p'
81 89 if not html_source:
82 90 return html_source
83 91
84 try:
85 from lxml.html import fromstring
86 from lxml.html import tostring
87 except ImportError:
88 log.exception('Failed to import lxml')
92 if not fromstring and tostring:
89 93 return html_source
90 94
91 95 try:
@@ -210,6 +214,8 b' class MarkupRenderer(object):'
210 214 URL_PAT = re.compile(r'(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]'
211 215 r'|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)')
212 216
217 MENTION_PAT = re.compile(MENTIONS_REGEX)
218
213 219 extensions = ['markdown.extensions.codehilite', 'markdown.extensions.extra',
214 220 'markdown.extensions.def_list', 'markdown.extensions.sane_lists']
215 221
@@ -348,6 +354,26 b' class MarkupRenderer(object):'
348 354 return cls.URL_PAT.sub(url_func, text)
349 355
350 356 @classmethod
357 def convert_mentions(cls, text, mode):
358 mention_pat = cls.MENTION_PAT
359
360 def wrapp(match_obj):
361 uname = match_obj.groups()[0]
362 hovercard_url = "pyroutes.url('hovercard_username', {'username': '%s'});" % uname
363
364 if mode == 'markdown':
365 tmpl = '<strong class="tooltip-hovercard" data-hovercard-alt="{uname}" data-hovercard-url="{hovercard_url}">@{uname}</strong>'
366 elif mode == 'rst':
367 tmpl = ' **@{uname}** '
368 else:
369 raise ValueError('mode must be rst or markdown')
370
371 return tmpl.format(**{'uname': uname,
372 'hovercard_url': hovercard_url})
373
374 return mention_pat.sub(wrapp, text).strip()
375
376 @classmethod
351 377 def plain(cls, source, universal_newline=True, leading_newline=True):
352 378 source = safe_unicode(source)
353 379 if universal_newline:
@@ -378,12 +404,7 b' class MarkupRenderer(object):'
378 404 cls.extensions, cls.output_format)
379 405
380 406 if mentions:
381 mention_pat = re.compile(MENTIONS_REGEX)
382
383 def wrapp(match_obj):
384 uname = match_obj.groups()[0]
385 return ' **@%(uname)s** ' % {'uname': uname}
386 mention_hl = mention_pat.sub(wrapp, source).strip()
407 mention_hl = cls.convert_mentions(source, mode='markdown')
387 408 # we extracted mentions render with this using Mentions false
388 409 return cls.markdown(mention_hl, safe=safe, flavored=flavored,
389 410 mentions=False)
@@ -409,12 +430,7 b' class MarkupRenderer(object):'
409 430 @classmethod
410 431 def rst(cls, source, safe=True, mentions=False, clean_html=False):
411 432 if mentions:
412 mention_pat = re.compile(MENTIONS_REGEX)
413
414 def wrapp(match_obj):
415 uname = match_obj.groups()[0]
416 return ' **@%(uname)s** ' % {'uname': uname}
417 mention_hl = mention_pat.sub(wrapp, source).strip()
433 mention_hl = cls.convert_mentions(source, mode='rst')
418 434 # we extracted mentions render with this using Mentions false
419 435 return cls.rst(mention_hl, safe=safe, mentions=False)
420 436
@@ -443,7 +459,7 b' class MarkupRenderer(object):'
443 459 except Exception:
444 460 log.exception('Error when rendering RST')
445 461 if safe:
446 log.debug('Fallbacking to render in plain mode')
462 log.debug('Fallback to render in plain mode')
447 463 return cls.plain(source)
448 464 else:
449 465 raise
@@ -133,15 +133,16 b' class SimpleVCS(object):'
133 133 self.config = config
134 134 # re-populated by specialized middleware
135 135 self.repo_vcs_config = base.Config()
136 self.rhodecode_settings = SettingsModel().get_all_settings(cache=True)
137 136
138 registry.rhodecode_settings = self.rhodecode_settings
137 rc_settings = SettingsModel().get_all_settings(cache=True, from_request=False)
138 realm = rc_settings.get('rhodecode_realm') or 'RhodeCode AUTH'
139
139 140 # authenticate this VCS request using authfunc
140 141 auth_ret_code_detection = \
141 142 str2bool(self.config.get('auth_ret_code_detection', False))
142 143 self.authenticate = BasicAuth(
143 144 '', authenticate, registry, config.get('auth_ret_code'),
144 auth_ret_code_detection)
145 auth_ret_code_detection, rc_realm=realm)
145 146 self.ip_addr = '0.0.0.0'
146 147
147 148 @LazyProperty
@@ -32,10 +32,14 b' def BeakerSessionFactoryConfig(**options'
32 32
33 33 def session_callback(request, response):
34 34 exception = getattr(request, 'exception', None)
35 if (exception is None or self._cookie_on_exception) and self.accessed():
35 file_response = getattr(request, '_file_response', None)
36
37 if file_response is None \
38 and (exception is None or self._cookie_on_exception) \
39 and self.accessed():
36 40 self.persist()
37 41 headers = self.__dict__['_headers']
38 if headers['set_cookie'] and headers['cookie_out']:
42 if headers.get('set_cookie') and headers.get('cookie_out'):
39 43 response.headerlist.append(('Set-Cookie', headers['cookie_out']))
40 44 request.add_response_callback(session_callback)
41 45
@@ -148,6 +148,15 b' class HGUpdateCaches(MaintenanceTask):'
148 148 return res
149 149
150 150
151 class HGRebuildFnCaches(MaintenanceTask):
152 human_name = 'HG rebuild fn caches'
153
154 def run(self):
155 instance = self.db_repo.scm_instance()
156 res = instance.hg_rebuild_fn_cache()
157 return res
158
159
151 160 class SVNVerify(MaintenanceTask):
152 161 human_name = 'SVN Verify repo'
153 162
@@ -162,7 +171,7 b' class RepoMaintenance(object):'
162 171 Performs maintenance of repository based on it's type
163 172 """
164 173 tasks = {
165 'hg': [HGVerify, HGUpdateCaches],
174 'hg': [HGVerify, HGUpdateCaches, HGRebuildFnCaches],
166 175 'git': [GitFSCK, GitGC, GitRepack],
167 176 'svn': [SVNVerify],
168 177 }
@@ -432,8 +432,11 b' class GitCommit(base.BaseCommit):'
432 432 """
433 433 if not self.parents:
434 434 return list(self._get_file_nodes())
435 return AddedFileNodesGenerator(
436 [n for n in self._get_paths_for_status('added')], self)
435 return AddedFileNodesGenerator(self.added_paths, self)
436
437 @LazyProperty
438 def added_paths(self):
439 return [n for n in self._get_paths_for_status('added')]
437 440
438 441 @LazyProperty
439 442 def changed(self):
@@ -442,8 +445,11 b' class GitCommit(base.BaseCommit):'
442 445 """
443 446 if not self.parents:
444 447 return []
445 return ChangedFileNodesGenerator(
446 [n for n in self._get_paths_for_status('modified')], self)
448 return ChangedFileNodesGenerator(self.changed_paths, self)
449
450 @LazyProperty
451 def changed_paths(self):
452 return [n for n in self._get_paths_for_status('modified')]
447 453
448 454 @LazyProperty
449 455 def removed(self):
@@ -452,8 +458,11 b' class GitCommit(base.BaseCommit):'
452 458 """
453 459 if not self.parents:
454 460 return []
455 return RemovedFileNodesGenerator(
456 [n for n in self._get_paths_for_status('deleted')], self)
461 return RemovedFileNodesGenerator(self.removed_paths, self)
462
463 @LazyProperty
464 def removed_paths(self):
465 return [n for n in self._get_paths_for_status('deleted')]
457 466
458 467 def _get_submodule_url(self, submodule_path):
459 468 git_modules_path = '.gitmodules'
@@ -472,7 +481,7 b' class GitCommit(base.BaseCommit):'
472 481 for line in _content.splitlines():
473 482 yield line
474 483
475 parser = configparser.ConfigParser()
484 parser = configparser.RawConfigParser()
476 485 parser.read_file(iter_content(submodules_node.content))
477 486
478 487 for section in parser.sections():
@@ -372,18 +372,30 b' class MercurialCommit(base.BaseCommit):'
372 372 """
373 373 Returns list of added ``FileNode`` objects.
374 374 """
375 return AddedFileNodesGenerator([n for n in self.status[1]], self)
375 return AddedFileNodesGenerator(self.added_paths, self)
376
377 @LazyProperty
378 def added_paths(self):
379 return [n for n in self.status[1]]
376 380
377 381 @property
378 382 def changed(self):
379 383 """
380 384 Returns list of modified ``FileNode`` objects.
381 385 """
382 return ChangedFileNodesGenerator([n for n in self.status[0]], self)
386 return ChangedFileNodesGenerator(self.changed_paths, self)
387
388 @LazyProperty
389 def changed_paths(self):
390 return [n for n in self.status[0]]
383 391
384 392 @property
385 393 def removed(self):
386 394 """
387 395 Returns list of removed ``FileNode`` objects.
388 396 """
389 return RemovedFileNodesGenerator([n for n in self.status[2]], self)
397 return RemovedFileNodesGenerator(self.removed_paths, self)
398
399 @LazyProperty
400 def removed_paths(self):
401 return [n for n in self.status[2]]
@@ -290,6 +290,12 b' class MercurialRepository(BaseRepository'
290 290 self._remote.invalidate_vcs_cache()
291 291 return update_cache
292 292
293 def hg_rebuild_fn_cache(self):
294 update_cache = self._remote.hg_rebuild_fn_cache()
295
296 self._remote.invalidate_vcs_cache()
297 return update_cache
298
293 299 def get_common_ancestor(self, commit_id1, commit_id2, repo2):
294 300 if commit_id1 == commit_id2:
295 301 return commit_id1
@@ -218,18 +218,27 b' class SubversionCommit(base.BaseCommit):'
218 218
219 219 @property
220 220 def added(self):
221 return nodes.AddedFileNodesGenerator(
222 self._changes_cache['added'], self)
221 return nodes.AddedFileNodesGenerator(self.added_paths, self)
222
223 @LazyProperty
224 def added_paths(self):
225 return [n for n in self._changes_cache['added']]
223 226
224 227 @property
225 228 def changed(self):
226 return nodes.ChangedFileNodesGenerator(
227 self._changes_cache['changed'], self)
229 return nodes.ChangedFileNodesGenerator(self.changed_paths, self)
230
231 @LazyProperty
232 def changed_paths(self):
233 return [n for n in self._changes_cache['changed']]
228 234
229 235 @property
230 236 def removed(self):
231 return nodes.RemovedFileNodesGenerator(
232 self._changes_cache['removed'], self)
237 return nodes.RemovedFileNodesGenerator(self.removed_paths, self)
238
239 @LazyProperty
240 def removed_paths(self):
241 return [n for n in self._changes_cache['removed']]
233 242
234 243
235 244 def _date_from_svn_properties(properties):
@@ -18,6 +18,19 b''
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 DEFAULTS = {
22 'encodings_map': {'.gz': 'gzip',
23 '.Z': 'compress',
24 '.bz2': 'bzip2',
25 '.xz': 'xz'},
26 'suffix_map': {'.svgz': '.svg.gz',
27 '.tgz': '.tar.gz',
28 '.taz': '.tar.gz',
29 '.tz': '.tar.gz',
30 '.tbz2': '.tar.bz2',
31 '.txz': '.tar.xz'},
32 }
33
21 34 TYPES_MAP = [
22 35 {'.jpg': 'image/jpg',
23 36 '.mid': 'audio/midi',
@@ -1203,4 +1216,6 b' def get_mimetypes_db(extra_types=None):'
1203 1216 types_map[1].update(extra_types)
1204 1217 db = mimetypes.MimeTypes()
1205 1218 db.types_map = types_map
1219 db.encodings_map.update(DEFAULTS['encodings_map'])
1220 db.suffix_map.update(DEFAULTS['suffix_map'])
1206 1221 return db
@@ -1025,6 +1025,17 b' class User(Base, BaseModel):'
1025 1025 return qry.all()
1026 1026
1027 1027 @classmethod
1028 def get_all_user_ids(cls, only_active=True):
1029 """
1030 Returns all users IDs
1031 """
1032 qry = Session().query(User.user_id)
1033
1034 if only_active:
1035 qry = qry.filter(User.active == true())
1036 return [x.user_id for x in qry]
1037
1038 @classmethod
1028 1039 def get_default_user(cls, cache=False, refresh=False):
1029 1040 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
1030 1041 if user is None:
@@ -3890,8 +3901,8 b' class _SetState(object):'
3890 3901 self._current_state = None
3891 3902
3892 3903 def __enter__(self):
3893 log.debug('StateLock: entering set state context, setting state to: `%s`',
3894 self._pr_state)
3904 log.debug('StateLock: entering set state context of pr %s, setting state to: `%s`',
3905 self._pr, self._pr_state)
3895 3906 self.set_pr_state(self._pr_state)
3896 3907 return self
3897 3908
@@ -3901,8 +3912,9 b' class _SetState(object):'
3901 3912 return None
3902 3913
3903 3914 self.set_pr_state(self._org_state)
3904 log.debug('StateLock: exiting set state context, setting state to: `%s`',
3905 self._org_state)
3915 log.debug('StateLock: exiting set state context of pr %s, setting state to: `%s`',
3916 self._pr, self._org_state)
3917
3906 3918 @property
3907 3919 def state(self):
3908 3920 return self._current_state
@@ -4285,6 +4297,7 b' class PullRequest(Base, _PullRequestBase'
4285 4297 def __json__(self):
4286 4298 return {
4287 4299 'revisions': self.revisions,
4300 'versions': self.versions_count
4288 4301 }
4289 4302
4290 4303 def calculated_review_status(self):
@@ -4307,6 +4320,14 b' class PullRequest(Base, _PullRequestBase'
4307 4320 vcs_obj = self.target_repo.scm_instance()
4308 4321 return vcs_obj.get_shadow_instance(shadow_repository_path)
4309 4322
4323 @property
4324 def versions_count(self):
4325 """
4326 return number of versions this PR have, e.g a PR that once been
4327 updated will have 2 versions
4328 """
4329 return self.versions.count() + 1
4330
4310 4331
4311 4332 class PullRequestVersion(Base, _PullRequestBase):
4312 4333 __tablename__ = 'pull_request_versions'
@@ -21,8 +21,7 b''
21 21 """
22 22 permissions model for RhodeCode
23 23 """
24
25
24 import collections
26 25 import logging
27 26 import traceback
28 27
@@ -557,6 +556,27 b' class PermissionModel(BaseModel):'
557 556 self.sa.rollback()
558 557 raise
559 558
559 def get_users_with_repo_write(self, db_repo):
560 write_plus = ['repository.write', 'repository.admin']
561 default_user_id = User.get_default_user().user_id
562 user_write_permissions = collections.OrderedDict()
563
564 # write+ and DEFAULT user for inheritance
565 for perm in db_repo.permissions():
566 if perm.permission in write_plus or perm.user_id == default_user_id:
567 user_write_permissions[perm.user_id] = perm
568 return user_write_permissions
569
570 def get_user_groups_with_repo_write(self, db_repo):
571 write_plus = ['repository.write', 'repository.admin']
572 user_group_write_permissions = collections.OrderedDict()
573
574 # write+ and DEFAULT user for inheritance
575 for p in db_repo.permission_user_groups():
576 if p.permission in write_plus:
577 user_group_write_permissions[p.users_group_id] = p
578 return user_group_write_permissions
579
560 580 def trigger_permission_flush(self, affected_user_ids):
561 581 events.trigger(events.UserPermissionsChange(affected_user_ids))
562 582
@@ -26,6 +26,8 b' pull request model for RhodeCode'
26 26
27 27 import json
28 28 import logging
29 import os
30
29 31 import datetime
30 32 import urllib
31 33 import collections
@@ -632,6 +634,7 b' class PullRequestModel(BaseModel):'
632 634 repo_id = pull_request.target_repo.repo_id
633 635 use_rebase = self._use_rebase_for_merging(pull_request)
634 636 close_branch = self._close_branch_before_merging(pull_request)
637 user_name = self._user_name_for_merging(pull_request, user)
635 638
636 639 target_ref = self._refresh_reference(
637 640 pull_request.target_ref_parts, target_vcs)
@@ -647,7 +650,6 b' class PullRequestModel(BaseModel):'
647 650 target_vcs.config.set(
648 651 'rhodecode', 'RC_SCM_DATA', json.dumps(extras))
649 652
650 user_name = user.short_contact
651 653 merge_state = target_vcs.merge(
652 654 repo_id, workspace_id, target_ref, source_vcs,
653 655 pull_request.source_ref_parts,
@@ -1664,6 +1666,16 b' class PullRequestModel(BaseModel):'
1664 1666
1665 1667 return False
1666 1668
1669 def _user_name_for_merging(self, pull_request, user):
1670 env_user_name_attr = os.environ.get('RC_MERGE_USER_NAME_ATTR', '')
1671 if env_user_name_attr and hasattr(user, env_user_name_attr):
1672 user_name_attr = env_user_name_attr
1673 else:
1674 user_name_attr = 'short_contact'
1675
1676 user_name = getattr(user, user_name_attr)
1677 return user_name
1678
1667 1679 def _close_branch_before_merging(self, pull_request):
1668 1680 repo_type = pull_request.target_repo.repo_type
1669 1681 if repo_type == 'hg':
@@ -38,8 +38,7 b' from rhodecode.lib.user_log_filter impor'
38 38 from rhodecode.lib.utils import make_db_config
39 39 from rhodecode.lib.utils2 import (
40 40 safe_str, safe_unicode, remove_prefix, obfuscate_url_pw,
41 get_current_rhodecode_user, safe_int, datetime_to_time,
42 action_logger_generic)
41 get_current_rhodecode_user, safe_int, action_logger_generic)
43 42 from rhodecode.lib.vcs.backends import get_backend
44 43 from rhodecode.model import BaseModel
45 44 from rhodecode.model.db import (
@@ -199,9 +198,11 b' class RepoModel(BaseModel):'
199 198
200 199 def get_repos_as_dict(self, repo_list=None, admin=False,
201 200 super_user_actions=False, short_name=None):
201
202 202 _render = get_current_request().get_partial_renderer(
203 203 'rhodecode:templates/data_table/_dt_elements.mako')
204 204 c = _render.get_call_context()
205 h = _render.get_helpers()
205 206
206 207 def quick_menu(repo_name):
207 208 return _render('quick_menu', repo_name)
@@ -258,7 +259,7 b' class RepoModel(BaseModel):'
258 259 "name": repo_lnk(repo.repo_name, repo.repo_type, repo.repo_state,
259 260 repo.private, repo.archived, repo.fork),
260 261
261 "desc": desc(repo.description),
262 "desc": desc(h.escape(repo.description)),
262 263
263 264 "last_change": last_change(repo.updated_on),
264 265
@@ -619,13 +620,26 b' class RepoModel(BaseModel):'
619 620 changes = {
620 621 'added': [],
621 622 'updated': [],
622 'deleted': []
623 'deleted': [],
624 'default_user_changed': None
623 625 }
626
627 repo = self._get_repo(repo)
628
624 629 # update permissions
625 630 for member_id, perm, member_type in perm_updates:
626 631 member_id = int(member_id)
627 632 if member_type == 'user':
628 633 member_name = User.get(member_id).username
634 if member_name == User.DEFAULT_USER:
635 # NOTE(dan): detect if we changed permissions for default user
636 perm_obj = self.sa.query(UserRepoToPerm) \
637 .filter(UserRepoToPerm.user_id == member_id) \
638 .filter(UserRepoToPerm.repository == repo) \
639 .scalar()
640 if perm_obj and perm_obj.permission.permission_name != perm:
641 changes['default_user_changed'] = True
642
629 643 # this updates also current one if found
630 644 self.grant_user_permission(
631 645 repo=repo, user=member_id, perm=perm)
@@ -41,7 +41,7 b' from rhodecode.model.db import (_hash_ke'
41 41 UserGroup, Repository)
42 42 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
43 43 from rhodecode.lib.caching_query import FromCache
44 from rhodecode.lib.utils2 import action_logger_generic, datetime_to_time
44 from rhodecode.lib.utils2 import action_logger_generic
45 45
46 46 log = logging.getLogger(__name__)
47 47
@@ -353,7 +353,8 b' class RepoGroupModel(BaseModel):'
353 353 changes = {
354 354 'added': [],
355 355 'updated': [],
356 'deleted': []
356 'deleted': [],
357 'default_user_changed': None
357 358 }
358 359
359 360 def _set_perm_user(obj, user, perm):
@@ -430,6 +431,15 b' class RepoGroupModel(BaseModel):'
430 431 member_id = int(member_id)
431 432 if member_type == 'user':
432 433 member_name = User.get(member_id).username
434 if isinstance(obj, RepoGroup) and obj == repo_group and member_name == User.DEFAULT_USER:
435 # NOTE(dan): detect if we changed permissions for default user
436 perm_obj = self.sa.query(UserRepoGroupToPerm) \
437 .filter(UserRepoGroupToPerm.user_id == member_id) \
438 .filter(UserRepoGroupToPerm.group == repo_group) \
439 .scalar()
440 if perm_obj and perm_obj.permission.permission_name != perm:
441 changes['default_user_changed'] = True
442
433 443 # this updates also current one if found
434 444 _set_perm_user(obj, user=member_id, perm=perm)
435 445 elif member_type == 'user_group':
@@ -698,8 +708,6 b' class RepoGroupModel(BaseModel):'
698 708 for repo_group in repo_groups:
699 709 repo_group.update_commit_cache()
700 710
701
702
703 711 def get_repo_groups_as_dict(self, repo_group_list=None, admin=False,
704 712 super_user_actions=False):
705 713
@@ -753,7 +761,7 b' class RepoGroupModel(BaseModel):'
753 761 "last_changeset": "",
754 762 "last_changeset_raw": "",
755 763
756 "desc": desc(group.group_description, group.personal),
764 "desc": desc(h.escape(group.group_description), group.personal),
757 765 "top_level_repos": 0,
758 766 "owner": user_profile(group.User.username)
759 767 }
@@ -21,9 +21,11 b''
21 21 import os
22 22 import hashlib
23 23 import logging
24 import re
24 25 from collections import namedtuple
25 26 from functools import wraps
26 27 import bleach
28 from pyramid.threadlocal import get_current_request, get_current_registry
27 29
28 30 from rhodecode.lib import rc_cache
29 31 from rhodecode.lib.utils2 import (
@@ -210,7 +212,23 b' class SettingsModel(BaseModel):'
210 212 invalidation_namespace = CacheKey.SETTINGS_INVALIDATION_NAMESPACE
211 213 CacheKey.set_invalidate(invalidation_namespace)
212 214
213 def get_all_settings(self, cache=False):
215 def get_all_settings(self, cache=False, from_request=True):
216 from rhodecode.authentication.base import get_authn_registry
217
218 # defines if we use GLOBAL, or PER_REPO
219 repo = self._get_repo(self.repo) if self.repo else None
220 key = "settings_repo.{}".format(repo.repo_id) if repo else "settings_app"
221
222 # initially try the requests context, this is the fastest
223 # we only fetch global config
224 if from_request:
225 request = get_current_request()
226
227 if request and not repo and hasattr(request, 'call_context') and hasattr(request.call_context, 'rc_config'):
228 rc_config = request.call_context.rc_config
229 if rc_config:
230 return rc_config
231
214 232 region = rc_cache.get_or_create_region('sql_cache_short')
215 233 invalidation_namespace = CacheKey.SETTINGS_INVALIDATION_NAMESPACE
216 234
@@ -226,9 +244,6 b' class SettingsModel(BaseModel):'
226 244 }
227 245 return settings
228 246
229 repo = self._get_repo(self.repo) if self.repo else None
230 key = "settings_repo.{}".format(repo.repo_id) if repo else "settings_app"
231
232 247 inv_context_manager = rc_cache.InvalidationContext(
233 248 uid='cache_settings', invalidation_namespace=invalidation_namespace)
234 249 with inv_context_manager as invalidation_context:
@@ -240,6 +255,11 b' class SettingsModel(BaseModel):'
240 255 # are anyway very short lived and it's a safest way.
241 256 region = rc_cache.get_or_create_region('sql_cache_short')
242 257 region.invalidate()
258 registry = get_current_registry()
259 if registry:
260 authn_registry = get_authn_registry(registry)
261 if authn_registry:
262 authn_registry.invalidate_plugins_for_auth()
243 263
244 264 result = _get_all_settings('rhodecode_settings', key)
245 265 log.debug('Fetching app settings for key: %s took: %.4fs', key,
@@ -360,9 +380,15 b' class IssueTrackerSettingsModel(object):'
360 380 for uid in issuetracker_entries:
361 381 url_data = qs.get(self._get_keyname('url', uid, 'rhodecode_'))
362 382
383 pat = qs.get(self._get_keyname('pat', uid, 'rhodecode_'))
384 try:
385 pat_compiled = re.compile(r'%s' % pat)
386 except re.error:
387 pat_compiled = None
388
363 389 issuetracker_entries[uid] = AttributeDict({
364 'pat': qs.get(
365 self._get_keyname('pat', uid, 'rhodecode_')),
390 'pat': pat,
391 'pat_compiled': pat_compiled,
366 392 'url': url_cleaner(
367 393 qs.get(self._get_keyname('url', uid, 'rhodecode_')) or ''),
368 394 'pref': bleach.clean(
@@ -38,6 +38,7 b' log = logging.getLogger(__name__)'
38 38
39 39 class SshKeyModel(BaseModel):
40 40 cls = UserSshKeys
41 DEFAULT_PRIVATE_KEY_FORMAT = 'pkcs8'
41 42
42 43 def parse_key(self, key_data):
43 44 """
@@ -66,16 +67,23 b' class SshKeyModel(BaseModel):'
66 67 log.error("Key Parse error: %s", err)
67 68 raise
68 69
69 def generate_keypair(self, comment=None):
70 def generate_keypair(self, comment=None, private_format=DEFAULT_PRIVATE_KEY_FORMAT):
70 71
71 72 key = rsa.generate_private_key(
72 73 backend=crypto_default_backend(),
73 74 public_exponent=65537,
74 75 key_size=2048
75 76 )
77 if private_format == self.DEFAULT_PRIVATE_KEY_FORMAT:
78 private_format = crypto_serialization.PrivateFormat.PKCS8
79 else:
80 # legacy format that can be used by older systems, use if pkcs8 have
81 # problems
82 private_format = crypto_serialization.PrivateFormat.TraditionalOpenSSL
83
76 84 private_key = key.private_bytes(
77 85 crypto_serialization.Encoding.PEM,
78 crypto_serialization.PrivateFormat.PKCS8,
86 private_format,
79 87 crypto_serialization.NoEncryption())
80 88 public_key = key.public_key().public_bytes(
81 89 crypto_serialization.Encoding.OpenSSH,
@@ -134,14 +134,11 b' div.markdown-block h2 {'
134 134 div.markdown-block h1 {
135 135 font-size: 32px;
136 136 margin: 15px 0 15px 0 !important;
137 padding-bottom: 5px !important;
138 137 }
139 138
140 139 div.markdown-block h2 {
141 140 font-size: 24px !important;
142 141 margin: 34px 0 10px 0 !important;
143 padding-top: 15px !important;
144 padding-bottom: 8px !important;
145 142 }
146 143
147 144 div.markdown-block h3 {
@@ -174,6 +171,21 b' div.markdown-block hr {'
174 171 margin-bottom: 13px;
175 172 }
176 173
174 div.markdown-block blockquote {
175 color: #424242 !important;
176 padding: 8px 21px;
177 margin: 12px 0;
178 border-left: 4px solid @grey6;
179 }
180
181 div.markdown-block blockquote p {
182 color: #424242 !important;
183 padding: 0 !important;
184 margin: 0 !important;
185 line-height: 1.5;
186 }
187
188
177 189 div.markdown-block ol,
178 190 div.markdown-block ul,
179 191 div.markdown-block p,
@@ -181,13 +193,11 b' div.markdown-block blockquote,'
181 193 div.markdown-block dl,
182 194 div.markdown-block li,
183 195 div.markdown-block table {
184 margin: 3px 0px 13px 0px !important;
185 196 color: #424242 !important;
186 197 font-size: 13px !important;
187 198 font-family: @text-regular;
188 199 font-weight: normal !important;
189 200 overflow: visible !important;
190 line-height: 140% !important;
191 201 }
192 202
193 203 div.markdown-block pre {
@@ -245,14 +255,6 b' div.markdown-block ol li {'
245 255 list-style: decimal !important;
246 256 }
247 257
248 /*
249 div.markdown-block a,
250 div.markdown-block a:visited {
251 color: #4183C4 !important;
252 background-color: inherit;
253 text-decoration: none;
254 }
255 */
256 258
257 259 div.markdown-block #message {
258 260 .border-radius(@border-radius);
@@ -87,6 +87,10 b' body {'
87 87 border-left: @border-thickness solid @border-default-color;
88 88 }
89 89
90 .cursor-pointer {
91 cursor: pointer;
92 }
93
90 94 input + .action-link, .action-link.first{
91 95 border-left: none;
92 96 }
@@ -3,9 +3,13 b''
3 3 .readme-title {
4 4 border: 1px solid @grey6;
5 5 padding: 10px 5px;
6 font-weight: 600;
7 6 margin-top: 30px;
8 7 margin-bottom: -1px;
8
9 a {
10 font-weight: 600;
11 font-size: 13px
12 }
9 13 }
10 14
11 15 div.readme_box {
@@ -34,14 +38,11 b' div.readme_box h2 {'
34 38 div.readme_box h1 {
35 39 font-size: 32px;
36 40 margin: 15px 0 15px 0 !important;
37 padding-bottom: 5px !important;
38 41 }
39 42
40 43 div.readme_box h2 {
41 44 font-size: 24px !important;
42 45 margin: 34px 0 10px 0 !important;
43 padding-top: 15px !important;
44 padding-bottom: 8px !important;
45 46 }
46 47
47 48 div.readme_box h3 {
@@ -74,6 +75,20 b' div.readme_box hr {'
74 75 margin-bottom: 13px;
75 76 }
76 77
78 div.readme_box blockquote {
79 color: #424242 !important;
80 padding: 8px 21px;
81 margin: 12px 0;
82 border-left: 4px solid @grey6;
83 }
84
85 div.readme_box blockquote p {
86 color: #424242 !important;
87 padding: 0 !important;
88 margin: 0 !important;
89 line-height: 1.5;
90 }
91
77 92 div.readme_box ol,
78 93 div.readme_box ul,
79 94 div.readme_box p,
@@ -81,13 +96,11 b' div.readme_box blockquote,'
81 96 div.readme_box dl,
82 97 div.readme_box li,
83 98 div.readme_box table {
84 margin: 3px 0px 13px 0px !important;
85 99 color: #424242 !important;
86 100 font-size: 13px !important;
87 101 font-family: @text-regular;
88 102 font-weight: normal !important;
89 103 overflow: visible !important;
90 line-height: 140% !important;
91 104 }
92 105
93 106 div.readme_box pre {
@@ -373,6 +373,17 b' table#repo_list_table {'
373 373 min-width: 600px;
374 374 }
375 375
376 #no_grid_data {
377 text-align: center;
378 }
379
380 #grid_data_loading {
381 text-align: center;
382 font-weight: 600;
383 font-size: 16px;
384 padding: 80px 20px;
385 }
386
376 387 // Keyboard mappings
377 388 table.keyboard-mappings {
378 389 th {
@@ -538,5 +549,12 b' table.compare_view_commits {'
538 549 img{
539 550 vertical-align: middle;
540 551 }
552
553 &.td-expire {
554 width: 200px;
555 }
556 &.td-gist-type {
557 width: 100px;
558 }
541 559 }
542 560 }
@@ -593,7 +593,7 b' address {'
593 593 }
594 594
595 595 .help-block-inline {
596 margin: 0;
596 margin: 0 !important;
597 597 }
598 598
599 599 // help block text
@@ -11,28 +11,38 b' var _TM = {'
11 11 'All Authors': 'All Authors',
12 12 'All individual reviewers must vote.': 'All individual reviewers must vote.',
13 13 'All reviewers must vote.': 'All reviewers must vote.',
14 'Are you sure to close this pull request without merging?': 'Are you sure to close this pull request without merging?',
14 15 'At least {0} reviewer must vote.': 'At least {0} reviewer must vote.',
15 16 'At least {0} reviewers must vote.': 'At least {0} reviewers must vote.',
16 17 'Author is not allowed to be a reviewer.': 'Author is not allowed to be a reviewer.',
17 18 'Changed files': 'Changed files',
18 19 'Close': 'Close',
20 'Collapse all files': 'Collapse all files',
21 'Collapse {0} commit': 'Collapse {0} commit',
22 'Collapse {0} commits': 'Collapse {0} commits',
19 23 'Comment text will be set automatically based on currently selected status ({0}) ...': 'Comment text will be set automatically based on currently selected status ({0}) ...',
20 24 'Commit Authors are not allowed to be a reviewer.': 'Commit Authors are not allowed to be a reviewer.',
21 25 'Context file: ': 'Context file: ',
22 26 'Delete this comment?': 'Delete this comment?',
23 27 'Diff to Commit ': 'Diff to Commit ',
28 'Expand all files': 'Expand all files',
29 'Expand {0} commit': 'Expand {0} commit',
30 'Expand {0} commits': 'Expand {0} commits',
24 31 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.': 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.',
25 32 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.': 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.',
26 33 'Follow': 'Follow',
34 'Force updating...': 'Force updating...',
27 35 'Hide full context diff': 'Hide full context diff',
28 36 'Hide whitespace changes': 'Hide whitespace changes',
29 37 'Invite reviewers to this discussion': 'Invite reviewers to this discussion',
30 38 'Leave a comment on line {0}.': 'Leave a comment on line {0}.',
31 39 'Leave a comment, or click resolve button to resolve TODO comment #{0}': 'Leave a comment, or click resolve button to resolve TODO comment #{0}',
40 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}': 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}',
32 41 'Lifetime': 'Lifetime',
33 42 'Loading ...': 'Loading ...',
34 43 'Loading failed': 'Loading failed',
35 44 'Loading more results...': 'Loading more results...',
45 'Loading...': 'Loading...',
36 46 'No bookmarks available yet.': 'No bookmarks available yet.',
37 47 'No branches available yet.': 'No branches available yet.',
38 48 'No forks available yet.': 'No forks available yet.',
@@ -41,7 +51,9 b' var _TM = {'
41 51 'No matching files': 'No matching files',
42 52 'No pull requests available yet.': 'No pull requests available yet.',
43 53 'No repositories available yet.': 'No repositories available yet.',
54 'No repositories present.': 'No repositories present.',
44 55 'No repository groups available yet.': 'No repository groups available yet.',
56 'No repository groups present.': 'No repository groups present.',
45 57 'No results': 'No results',
46 58 'No ssh keys available yet.': 'No ssh keys available yet.',
47 59 'No tags available yet.': 'No tags available yet.',
@@ -125,9 +137,11 b' var _TM = {'
125 137 '{0} min': '{0} min',
126 138 '{0} month': '{0} month',
127 139 '{0} months': '{0} months',
140 '{0} of {1} repositories': '{0} of {1} repositories',
128 141 '{0} of {1} repository groups': '{0} of {1} repository groups',
129 142 '{0} out of {1} ssh keys': '{0} out of {1} ssh keys',
130 143 '{0} out of {1} users': '{0} out of {1} users',
144 '{0} repositories': '{0} repositories',
131 145 '{0} repository groups': '{0} repository groups',
132 146 '{0} results are available, use up and down arrow keys to navigate.': '{0} results are available, use up and down arrow keys to navigate.',
133 147 '{0} sec': '{0} sec',
@@ -11,28 +11,38 b' var _TM = {'
11 11 'All Authors': 'All Authors',
12 12 'All individual reviewers must vote.': 'All individual reviewers must vote.',
13 13 'All reviewers must vote.': 'All reviewers must vote.',
14 'Are you sure to close this pull request without merging?': 'Are you sure to close this pull request without merging?',
14 15 'At least {0} reviewer must vote.': 'At least {0} reviewer must vote.',
15 16 'At least {0} reviewers must vote.': 'At least {0} reviewers must vote.',
16 17 'Author is not allowed to be a reviewer.': 'Author is not allowed to be a reviewer.',
17 18 'Changed files': 'Changed files',
18 19 'Close': 'Close',
20 'Collapse all files': 'Collapse all files',
21 'Collapse {0} commit': 'Collapse {0} commit',
22 'Collapse {0} commits': 'Collapse {0} commits',
19 23 'Comment text will be set automatically based on currently selected status ({0}) ...': 'Comment text will be set automatically based on currently selected status ({0}) ...',
20 24 'Commit Authors are not allowed to be a reviewer.': 'Commit Authors are not allowed to be a reviewer.',
21 25 'Context file: ': 'Context file: ',
22 26 'Delete this comment?': 'Delete this comment?',
23 27 'Diff to Commit ': 'Diff to Commit ',
28 'Expand all files': 'Expand all files',
29 'Expand {0} commit': 'Expand {0} commit',
30 'Expand {0} commits': 'Expand {0} commits',
24 31 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.': 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.',
25 32 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.': 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.',
26 33 'Follow': 'Follow',
34 'Force updating...': 'Force updating...',
27 35 'Hide full context diff': 'Hide full context diff',
28 36 'Hide whitespace changes': 'Hide whitespace changes',
29 37 'Invite reviewers to this discussion': 'Invite reviewers to this discussion',
30 38 'Leave a comment on line {0}.': 'Leave a comment on line {0}.',
31 39 'Leave a comment, or click resolve button to resolve TODO comment #{0}': 'Leave a comment, or click resolve button to resolve TODO comment #{0}',
40 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}': 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}',
32 41 'Lifetime': 'Lebensdauer',
33 42 'Loading ...': 'Loading ...',
34 43 'Loading failed': 'Loading failed',
35 44 'Loading more results...': 'Loading more results...',
45 'Loading...': 'Loading...',
36 46 'No bookmarks available yet.': 'No bookmarks available yet.',
37 47 'No branches available yet.': 'No branches available yet.',
38 48 'No forks available yet.': 'No forks available yet.',
@@ -41,7 +51,9 b' var _TM = {'
41 51 'No matching files': 'No matching files',
42 52 'No pull requests available yet.': 'No pull requests available yet.',
43 53 'No repositories available yet.': 'No repositories available yet.',
54 'No repositories present.': 'No repositories present.',
44 55 'No repository groups available yet.': 'No repository groups available yet.',
56 'No repository groups present.': 'No repository groups present.',
45 57 'No results': 'No results',
46 58 'No ssh keys available yet.': 'No ssh keys available yet.',
47 59 'No tags available yet.': 'No tags available yet.',
@@ -125,9 +137,11 b' var _TM = {'
125 137 '{0} min': '{0} min',
126 138 '{0} month': '{0} month',
127 139 '{0} months': '{0} months',
140 '{0} of {1} repositories': '{0} of {1} repositories',
128 141 '{0} of {1} repository groups': '{0} of {1} repository groups',
129 142 '{0} out of {1} ssh keys': '{0} out of {1} ssh keys',
130 143 '{0} out of {1} users': '{0} out of {1} users',
144 '{0} repositories': '{0} repositories',
131 145 '{0} repository groups': '{0} repository groups',
132 146 '{0} results are available, use up and down arrow keys to navigate.': '{0} results are available, use up and down arrow keys to navigate.',
133 147 '{0} sec': '{0} sec',
@@ -11,28 +11,38 b' var _TM = {'
11 11 'All Authors': 'All Authors',
12 12 'All individual reviewers must vote.': 'All individual reviewers must vote.',
13 13 'All reviewers must vote.': 'All reviewers must vote.',
14 'Are you sure to close this pull request without merging?': 'Are you sure to close this pull request without merging?',
14 15 'At least {0} reviewer must vote.': 'At least {0} reviewer must vote.',
15 16 'At least {0} reviewers must vote.': 'At least {0} reviewers must vote.',
16 17 'Author is not allowed to be a reviewer.': 'Author is not allowed to be a reviewer.',
17 18 'Changed files': 'Changed files',
18 19 'Close': 'Close',
20 'Collapse all files': 'Collapse all files',
21 'Collapse {0} commit': 'Collapse {0} commit',
22 'Collapse {0} commits': 'Collapse {0} commits',
19 23 'Comment text will be set automatically based on currently selected status ({0}) ...': 'Comment text will be set automatically based on currently selected status ({0}) ...',
20 24 'Commit Authors are not allowed to be a reviewer.': 'Commit Authors are not allowed to be a reviewer.',
21 25 'Context file: ': 'Context file: ',
22 26 'Delete this comment?': 'Delete this comment?',
23 27 'Diff to Commit ': 'Diff to Commit ',
28 'Expand all files': 'Expand all files',
29 'Expand {0} commit': 'Expand {0} commit',
30 'Expand {0} commits': 'Expand {0} commits',
24 31 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.': 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.',
25 32 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.': 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.',
26 33 'Follow': 'Follow',
34 'Force updating...': 'Force updating...',
27 35 'Hide full context diff': 'Hide full context diff',
28 36 'Hide whitespace changes': 'Hide whitespace changes',
29 37 'Invite reviewers to this discussion': 'Invite reviewers to this discussion',
30 38 'Leave a comment on line {0}.': 'Leave a comment on line {0}.',
31 39 'Leave a comment, or click resolve button to resolve TODO comment #{0}': 'Leave a comment, or click resolve button to resolve TODO comment #{0}',
40 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}': 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}',
32 41 'Lifetime': 'Lifetime',
33 42 'Loading ...': 'Loading ...',
34 43 'Loading failed': 'Loading failed',
35 44 'Loading more results...': 'Loading more results...',
45 'Loading...': 'Loading...',
36 46 'No bookmarks available yet.': 'No bookmarks available yet.',
37 47 'No branches available yet.': 'No branches available yet.',
38 48 'No forks available yet.': 'No forks available yet.',
@@ -41,7 +51,9 b' var _TM = {'
41 51 'No matching files': 'No matching files',
42 52 'No pull requests available yet.': 'No pull requests available yet.',
43 53 'No repositories available yet.': 'No repositories available yet.',
54 'No repositories present.': 'No repositories present.',
44 55 'No repository groups available yet.': 'No repository groups available yet.',
56 'No repository groups present.': 'No repository groups present.',
45 57 'No results': 'No results',
46 58 'No ssh keys available yet.': 'No ssh keys available yet.',
47 59 'No tags available yet.': 'No tags available yet.',
@@ -125,9 +137,11 b' var _TM = {'
125 137 '{0} min': '{0} min',
126 138 '{0} month': '{0} month',
127 139 '{0} months': '{0} months',
140 '{0} of {1} repositories': '{0} of {1} repositories',
128 141 '{0} of {1} repository groups': '{0} of {1} repository groups',
129 142 '{0} out of {1} ssh keys': '{0} out of {1} ssh keys',
130 143 '{0} out of {1} users': '{0} out of {1} users',
144 '{0} repositories': '{0} repositories',
131 145 '{0} repository groups': '{0} repository groups',
132 146 '{0} results are available, use up and down arrow keys to navigate.': '{0} results are available, use up and down arrow keys to navigate.',
133 147 '{0} sec': '{0} sec',
@@ -11,28 +11,38 b' var _TM = {'
11 11 'All Authors': 'All Authors',
12 12 'All individual reviewers must vote.': 'All individual reviewers must vote.',
13 13 'All reviewers must vote.': 'All reviewers must vote.',
14 'Are you sure to close this pull request without merging?': 'Are you sure to close this pull request without merging?',
14 15 'At least {0} reviewer must vote.': 'At least {0} reviewer must vote.',
15 16 'At least {0} reviewers must vote.': 'At least {0} reviewers must vote.',
16 17 'Author is not allowed to be a reviewer.': 'Author is not allowed to be a reviewer.',
17 18 'Changed files': 'Changed files',
18 19 'Close': 'Close',
20 'Collapse all files': 'Collapse all files',
21 'Collapse {0} commit': 'Collapse {0} commit',
22 'Collapse {0} commits': 'Collapse {0} commits',
19 23 'Comment text will be set automatically based on currently selected status ({0}) ...': 'Comment text will be set automatically based on currently selected status ({0}) ...',
20 24 'Commit Authors are not allowed to be a reviewer.': 'Commit Authors are not allowed to be a reviewer.',
21 25 'Context file: ': 'Context file: ',
22 26 'Delete this comment?': 'Delete this comment?',
23 27 'Diff to Commit ': 'Diff to Commit ',
28 'Expand all files': 'Expand all files',
29 'Expand {0} commit': 'Expand {0} commit',
30 'Expand {0} commits': 'Expand {0} commits',
24 31 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.': 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.',
25 32 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.': 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.',
26 33 'Follow': 'Follow',
34 'Force updating...': 'Force updating...',
27 35 'Hide full context diff': 'Hide full context diff',
28 36 'Hide whitespace changes': 'Hide whitespace changes',
29 37 'Invite reviewers to this discussion': 'Invite reviewers to this discussion',
30 38 'Leave a comment on line {0}.': 'Leave a comment on line {0}.',
31 39 'Leave a comment, or click resolve button to resolve TODO comment #{0}': 'Leave a comment, or click resolve button to resolve TODO comment #{0}',
40 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}': 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}',
32 41 'Lifetime': 'De por vida',
33 42 'Loading ...': 'Loading ...',
34 43 'Loading failed': 'Loading failed',
35 44 'Loading more results...': 'Loading more results...',
45 'Loading...': 'Loading...',
36 46 'No bookmarks available yet.': 'No bookmarks available yet.',
37 47 'No branches available yet.': 'No branches available yet.',
38 48 'No forks available yet.': 'No forks available yet.',
@@ -41,7 +51,9 b' var _TM = {'
41 51 'No matching files': 'No matching files',
42 52 'No pull requests available yet.': 'No pull requests available yet.',
43 53 'No repositories available yet.': 'No repositories available yet.',
54 'No repositories present.': 'No repositories present.',
44 55 'No repository groups available yet.': 'No repository groups available yet.',
56 'No repository groups present.': 'No repository groups present.',
45 57 'No results': 'No results',
46 58 'No ssh keys available yet.': 'No ssh keys available yet.',
47 59 'No tags available yet.': 'No tags available yet.',
@@ -125,9 +137,11 b' var _TM = {'
125 137 '{0} min': '{0} min',
126 138 '{0} month': '{0} month',
127 139 '{0} months': '{0} months',
140 '{0} of {1} repositories': '{0} of {1} repositories',
128 141 '{0} of {1} repository groups': '{0} of {1} repository groups',
129 142 '{0} out of {1} ssh keys': '{0} out of {1} ssh keys',
130 143 '{0} out of {1} users': '{0} out of {1} users',
144 '{0} repositories': '{0} repositories',
131 145 '{0} repository groups': '{0} repository groups',
132 146 '{0} results are available, use up and down arrow keys to navigate.': '{0} results are available, use up and down arrow keys to navigate.',
133 147 '{0} sec': '{0} sec',
@@ -11,28 +11,38 b' var _TM = {'
11 11 'All Authors': 'All Authors',
12 12 'All individual reviewers must vote.': 'All individual reviewers must vote.',
13 13 'All reviewers must vote.': 'All reviewers must vote.',
14 'Are you sure to close this pull request without merging?': 'Are you sure to close this pull request without merging?',
14 15 'At least {0} reviewer must vote.': 'At least {0} reviewer must vote.',
15 16 'At least {0} reviewers must vote.': 'At least {0} reviewers must vote.',
16 17 'Author is not allowed to be a reviewer.': 'Author is not allowed to be a reviewer.',
17 18 'Changed files': 'Changed files',
18 19 'Close': 'Close',
20 'Collapse all files': 'Collapse all files',
21 'Collapse {0} commit': 'Collapse {0} commit',
22 'Collapse {0} commits': 'Collapse {0} commits',
19 23 'Comment text will be set automatically based on currently selected status ({0}) ...': 'Comment text will be set automatically based on currently selected status ({0}) ...',
20 24 'Commit Authors are not allowed to be a reviewer.': 'Commit Authors are not allowed to be a reviewer.',
21 25 'Context file: ': 'Context file: ',
22 26 'Delete this comment?': 'Delete this comment?',
23 27 'Diff to Commit ': 'Diff to Commit ',
28 'Expand all files': 'Expand all files',
29 'Expand {0} commit': 'Expand {0} commit',
30 'Expand {0} commits': 'Expand {0} commits',
24 31 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.': 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.',
25 32 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.': 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.',
26 33 'Follow': 'Follow',
34 'Force updating...': 'Force updating...',
27 35 'Hide full context diff': 'Hide full context diff',
28 36 'Hide whitespace changes': 'Hide whitespace changes',
29 37 'Invite reviewers to this discussion': 'Invite reviewers to this discussion',
30 38 'Leave a comment on line {0}.': 'Leave a comment on line {0}.',
31 39 'Leave a comment, or click resolve button to resolve TODO comment #{0}': 'Leave a comment, or click resolve button to resolve TODO comment #{0}',
40 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}': 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}',
32 41 'Lifetime': 'Durée de vie',
33 42 'Loading ...': 'Loading ...',
34 43 'Loading failed': 'Loading failed',
35 44 'Loading more results...': 'Loading more results...',
45 'Loading...': 'Loading...',
36 46 'No bookmarks available yet.': 'No bookmarks available yet.',
37 47 'No branches available yet.': 'No branches available yet.',
38 48 'No forks available yet.': 'No forks available yet.',
@@ -41,7 +51,9 b' var _TM = {'
41 51 'No matching files': 'No matching files',
42 52 'No pull requests available yet.': 'No pull requests available yet.',
43 53 'No repositories available yet.': 'No repositories available yet.',
54 'No repositories present.': 'No repositories present.',
44 55 'No repository groups available yet.': 'No repository groups available yet.',
56 'No repository groups present.': 'No repository groups present.',
45 57 'No results': 'No results',
46 58 'No ssh keys available yet.': 'No ssh keys available yet.',
47 59 'No tags available yet.': 'No tags available yet.',
@@ -125,9 +137,11 b' var _TM = {'
125 137 '{0} min': '{0} min',
126 138 '{0} month': '{0} month',
127 139 '{0} months': '{0} mois',
140 '{0} of {1} repositories': '{0} of {1} repositories',
128 141 '{0} of {1} repository groups': '{0} of {1} repository groups',
129 142 '{0} out of {1} ssh keys': '{0} out of {1} ssh keys',
130 143 '{0} out of {1} users': '{0} out of {1} users',
144 '{0} repositories': '{0} repositories',
131 145 '{0} repository groups': '{0} repository groups',
132 146 '{0} results are available, use up and down arrow keys to navigate.': '{0} results are available, use up and down arrow keys to navigate.',
133 147 '{0} sec': '{0} sec',
@@ -11,28 +11,38 b' var _TM = {'
11 11 'All Authors': 'All Authors',
12 12 'All individual reviewers must vote.': 'All individual reviewers must vote.',
13 13 'All reviewers must vote.': 'All reviewers must vote.',
14 'Are you sure to close this pull request without merging?': 'Are you sure to close this pull request without merging?',
14 15 'At least {0} reviewer must vote.': 'At least {0} reviewer must vote.',
15 16 'At least {0} reviewers must vote.': 'At least {0} reviewers must vote.',
16 17 'Author is not allowed to be a reviewer.': 'Author is not allowed to be a reviewer.',
17 18 'Changed files': 'Changed files',
18 19 'Close': 'Close',
20 'Collapse all files': 'Collapse all files',
21 'Collapse {0} commit': 'Collapse {0} commit',
22 'Collapse {0} commits': 'Collapse {0} commits',
19 23 'Comment text will be set automatically based on currently selected status ({0}) ...': 'Comment text will be set automatically based on currently selected status ({0}) ...',
20 24 'Commit Authors are not allowed to be a reviewer.': 'Commit Authors are not allowed to be a reviewer.',
21 25 'Context file: ': 'Context file: ',
22 26 'Delete this comment?': 'Delete this comment?',
23 27 'Diff to Commit ': 'Diff to Commit ',
28 'Expand all files': 'Expand all files',
29 'Expand {0} commit': 'Expand {0} commit',
30 'Expand {0} commits': 'Expand {0} commits',
24 31 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.': 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.',
25 32 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.': 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.',
26 33 'Follow': 'Segui',
34 'Force updating...': 'Force updating...',
27 35 'Hide full context diff': 'Hide full context diff',
28 36 'Hide whitespace changes': 'Hide whitespace changes',
29 37 'Invite reviewers to this discussion': 'Invite reviewers to this discussion',
30 38 'Leave a comment on line {0}.': 'Leave a comment on line {0}.',
31 39 'Leave a comment, or click resolve button to resolve TODO comment #{0}': 'Leave a comment, or click resolve button to resolve TODO comment #{0}',
40 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}': 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}',
32 41 'Lifetime': 'a vita',
33 42 'Loading ...': 'Caricamento ...',
34 43 'Loading failed': 'Loading failed',
35 44 'Loading more results...': 'Loading more results...',
45 'Loading...': 'Loading...',
36 46 'No bookmarks available yet.': 'No bookmarks available yet.',
37 47 'No branches available yet.': 'No branches available yet.',
38 48 'No forks available yet.': 'No forks available yet.',
@@ -41,7 +51,9 b' var _TM = {'
41 51 'No matching files': 'Nessuna corrispondenza tra i file',
42 52 'No pull requests available yet.': 'No pull requests available yet.',
43 53 'No repositories available yet.': 'No repositories available yet.',
54 'No repositories present.': 'No repositories present.',
44 55 'No repository groups available yet.': 'No repository groups available yet.',
56 'No repository groups present.': 'No repository groups present.',
45 57 'No results': 'Nessun risultato',
46 58 'No ssh keys available yet.': 'No ssh keys available yet.',
47 59 'No tags available yet.': 'No tags available yet.',
@@ -125,9 +137,11 b' var _TM = {'
125 137 '{0} min': '{0} min',
126 138 '{0} month': '{0} month',
127 139 '{0} months': '{0} months',
140 '{0} of {1} repositories': '{0} of {1} repositories',
128 141 '{0} of {1} repository groups': '{0} of {1} repository groups',
129 142 '{0} out of {1} ssh keys': '{0} out of {1} ssh keys',
130 143 '{0} out of {1} users': '{0} out of {1} users',
144 '{0} repositories': '{0} repositories',
131 145 '{0} repository groups': '{0} repository groups',
132 146 '{0} results are available, use up and down arrow keys to navigate.': '{0} results are available, use up and down arrow keys to navigate.',
133 147 '{0} sec': '{0} sec',
@@ -11,28 +11,38 b' var _TM = {'
11 11 'All Authors': 'All Authors',
12 12 'All individual reviewers must vote.': 'All individual reviewers must vote.',
13 13 'All reviewers must vote.': 'All reviewers must vote.',
14 'Are you sure to close this pull request without merging?': 'Are you sure to close this pull request without merging?',
14 15 'At least {0} reviewer must vote.': 'At least {0} reviewer must vote.',
15 16 'At least {0} reviewers must vote.': 'At least {0} reviewers must vote.',
16 17 'Author is not allowed to be a reviewer.': 'Author is not allowed to be a reviewer.',
17 18 'Changed files': 'Changed files',
18 19 'Close': 'Close',
20 'Collapse all files': 'Collapse all files',
21 'Collapse {0} commit': 'Collapse {0} commit',
22 'Collapse {0} commits': 'Collapse {0} commits',
19 23 'Comment text will be set automatically based on currently selected status ({0}) ...': '選択したステータス ({0}) を元にコメントが自動的に設定されます...',
20 24 'Commit Authors are not allowed to be a reviewer.': 'Commit Authors are not allowed to be a reviewer.',
21 25 'Context file: ': 'Context file: ',
22 26 'Delete this comment?': 'Delete this comment?',
23 27 'Diff to Commit ': 'Diff to Commit ',
28 'Expand all files': 'Expand all files',
29 'Expand {0} commit': 'Expand {0} commit',
30 'Expand {0} commits': 'Expand {0} commits',
24 31 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.': 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.',
25 32 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.': 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.',
26 33 'Follow': 'フォロー',
34 'Force updating...': 'Force updating...',
27 35 'Hide full context diff': 'Hide full context diff',
28 36 'Hide whitespace changes': 'Hide whitespace changes',
29 37 'Invite reviewers to this discussion': 'Invite reviewers to this discussion',
30 38 'Leave a comment on line {0}.': 'Leave a comment on line {0}.',
31 39 'Leave a comment, or click resolve button to resolve TODO comment #{0}': 'Leave a comment, or click resolve button to resolve TODO comment #{0}',
40 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}': 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}',
32 41 'Lifetime': '有効期間',
33 42 'Loading ...': '読み込み中...',
34 43 'Loading failed': '読み込み失敗',
35 44 'Loading more results...': '結果を読み込み中...',
45 'Loading...': 'Loading...',
36 46 'No bookmarks available yet.': 'まだブックマークがありません。',
37 47 'No branches available yet.': 'まだブランチがありません。',
38 48 'No forks available yet.': 'No forks available yet.',
@@ -41,7 +51,9 b' var _TM = {'
41 51 'No matching files': 'マッチするファイルはありません',
42 52 'No pull requests available yet.': 'まだプルリクエストがありません。',
43 53 'No repositories available yet.': 'まだリポジトリがありません。',
54 'No repositories present.': 'No repositories present.',
44 55 'No repository groups available yet.': 'まだリポジトリグループがありません。',
56 'No repository groups present.': 'No repository groups present.',
45 57 'No results': '結果がありません',
46 58 'No ssh keys available yet.': 'No ssh keys available yet.',
47 59 'No tags available yet.': 'まだタグがありません。',
@@ -125,9 +137,11 b' var _TM = {'
125 137 '{0} min': '{0} 分',
126 138 '{0} month': '{0} ヶ月',
127 139 '{0} months': '{0} months',
140 '{0} of {1} repositories': '{0} of {1} repositories',
128 141 '{0} of {1} repository groups': '{0} of {1} repository groups',
129 142 '{0} out of {1} ssh keys': '{0} out of {1} ssh keys',
130 143 '{0} out of {1} users': '{0} out of {1} users',
144 '{0} repositories': '{0} repositories',
131 145 '{0} repository groups': '{0} repository groups',
132 146 '{0} results are available, use up and down arrow keys to navigate.': '{0} 件の結果があります。矢印キーの上下で選択できます。',
133 147 '{0} sec': '{0} 秒',
@@ -5,28 +5,38 b''
5 5 _gettext('All Authors');
6 6 _gettext('All individual reviewers must vote.');
7 7 _gettext('All reviewers must vote.');
8 _gettext('Are you sure to close this pull request without merging?');
8 9 _gettext('At least {0} reviewer must vote.');
9 10 _gettext('At least {0} reviewers must vote.');
10 11 _gettext('Author is not allowed to be a reviewer.');
11 12 _gettext('Changed files');
12 13 _gettext('Close');
14 _gettext('Collapse all files');
15 _gettext('Collapse {0} commit');
16 _gettext('Collapse {0} commits');
13 17 _gettext('Comment text will be set automatically based on currently selected status ({0}) ...');
14 18 _gettext('Commit Authors are not allowed to be a reviewer.');
15 19 _gettext('Context file: ');
16 20 _gettext('Delete this comment?');
17 21 _gettext('Diff to Commit ');
22 _gettext('Expand all files');
23 _gettext('Expand {0} commit');
24 _gettext('Expand {0} commits');
18 25 _gettext('Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.');
19 26 _gettext('Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.');
20 27 _gettext('Follow');
28 _gettext('Force updating...');
21 29 _gettext('Hide full context diff');
22 30 _gettext('Hide whitespace changes');
23 31 _gettext('Invite reviewers to this discussion');
24 32 _gettext('Leave a comment on line {0}.');
25 33 _gettext('Leave a comment, or click resolve button to resolve TODO comment #{0}');
34 _gettext('Leave a resolution comment, or click resolve button to resolve TODO comment #{0}');
26 35 _gettext('Lifetime');
27 36 _gettext('Loading ...');
28 37 _gettext('Loading failed');
29 38 _gettext('Loading more results...');
39 _gettext('Loading...');
30 40 _gettext('No bookmarks available yet.');
31 41 _gettext('No branches available yet.');
32 42 _gettext('No forks available yet.');
@@ -35,7 +45,9 b''
35 45 _gettext('No matching files');
36 46 _gettext('No pull requests available yet.');
37 47 _gettext('No repositories available yet.');
48 _gettext('No repositories present.');
38 49 _gettext('No repository groups available yet.');
50 _gettext('No repository groups present.');
39 51 _gettext('No results');
40 52 _gettext('No ssh keys available yet.');
41 53 _gettext('No tags available yet.');
@@ -119,9 +131,11 b''
119 131 _gettext('{0} min');
120 132 _gettext('{0} month');
121 133 _gettext('{0} months');
134 _gettext('{0} of {1} repositories');
122 135 _gettext('{0} of {1} repository groups');
123 136 _gettext('{0} out of {1} ssh keys');
124 137 _gettext('{0} out of {1} users');
138 _gettext('{0} repositories');
125 139 _gettext('{0} repository groups');
126 140 _gettext('{0} results are available, use up and down arrow keys to navigate.');
127 141 _gettext('{0} sec');
@@ -11,28 +11,38 b' var _TM = {'
11 11 'All Authors': 'All Authors',
12 12 'All individual reviewers must vote.': 'All individual reviewers must vote.',
13 13 'All reviewers must vote.': 'All reviewers must vote.',
14 'Are you sure to close this pull request without merging?': 'Are you sure to close this pull request without merging?',
14 15 'At least {0} reviewer must vote.': 'At least {0} reviewer must vote.',
15 16 'At least {0} reviewers must vote.': 'At least {0} reviewers must vote.',
16 17 'Author is not allowed to be a reviewer.': 'Author is not allowed to be a reviewer.',
17 18 'Changed files': 'Changed files',
18 19 'Close': 'Zamknij',
20 'Collapse all files': 'Collapse all files',
21 'Collapse {0} commit': 'Collapse {0} commit',
22 'Collapse {0} commits': 'Collapse {0} commits',
19 23 'Comment text will be set automatically based on currently selected status ({0}) ...': 'Comment text will be set automatically based on currently selected status ({0}) ...',
20 24 'Commit Authors are not allowed to be a reviewer.': 'Commit Authors are not allowed to be a reviewer.',
21 25 'Context file: ': 'Context file: ',
22 26 'Delete this comment?': 'Delete this comment?',
23 27 'Diff to Commit ': 'Diff to Commit ',
28 'Expand all files': 'Expand all files',
29 'Expand {0} commit': 'Expand {0} commit',
30 'Expand {0} commits': 'Expand {0} commits',
24 31 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.': 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.',
25 32 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.': 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.',
26 33 'Follow': 'Obserwuj',
34 'Force updating...': 'Force updating...',
27 35 'Hide full context diff': 'Hide full context diff',
28 36 'Hide whitespace changes': 'Hide whitespace changes',
29 37 'Invite reviewers to this discussion': 'Invite reviewers to this discussion',
30 38 'Leave a comment on line {0}.': 'Leave a comment on line {0}.',
31 39 'Leave a comment, or click resolve button to resolve TODO comment #{0}': 'Leave a comment, or click resolve button to resolve TODO comment #{0}',
40 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}': 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}',
32 41 'Lifetime': 'Czas życia',
33 42 'Loading ...': 'Ładuję...',
34 43 'Loading failed': 'Loading failed',
35 44 'Loading more results...': 'Loading more results...',
45 'Loading...': 'Loading...',
36 46 'No bookmarks available yet.': 'No bookmarks available yet.',
37 47 'No branches available yet.': 'No branches available yet.',
38 48 'No forks available yet.': 'No forks available yet.',
@@ -41,7 +51,9 b' var _TM = {'
41 51 'No matching files': 'Nie ma plików pasujących',
42 52 'No pull requests available yet.': 'No pull requests available yet.',
43 53 'No repositories available yet.': 'No repositories available yet.',
54 'No repositories present.': 'No repositories present.',
44 55 'No repository groups available yet.': 'No repository groups available yet.',
56 'No repository groups present.': 'No repository groups present.',
45 57 'No results': 'No results',
46 58 'No ssh keys available yet.': 'No ssh keys available yet.',
47 59 'No tags available yet.': 'No tags available yet.',
@@ -125,9 +137,11 b' var _TM = {'
125 137 '{0} min': '{0} min',
126 138 '{0} month': '{0} month',
127 139 '{0} months': '{0} months',
140 '{0} of {1} repositories': '{0} of {1} repositories',
128 141 '{0} of {1} repository groups': '{0} of {1} repository groups',
129 142 '{0} out of {1} ssh keys': '{0} out of {1} ssh keys',
130 143 '{0} out of {1} users': '{0} out of {1} users',
144 '{0} repositories': '{0} repositories',
131 145 '{0} repository groups': '{0} repository groups',
132 146 '{0} results are available, use up and down arrow keys to navigate.': '{0} results are available, use up and down arrow keys to navigate.',
133 147 '{0} sec': '{0} sec',
@@ -11,28 +11,38 b' var _TM = {'
11 11 'All Authors': 'All Authors',
12 12 'All individual reviewers must vote.': 'All individual reviewers must vote.',
13 13 'All reviewers must vote.': 'All reviewers must vote.',
14 'Are you sure to close this pull request without merging?': 'Are you sure to close this pull request without merging?',
14 15 'At least {0} reviewer must vote.': 'At least {0} reviewer must vote.',
15 16 'At least {0} reviewers must vote.': 'At least {0} reviewers must vote.',
16 17 'Author is not allowed to be a reviewer.': 'Author is not allowed to be a reviewer.',
17 18 'Changed files': 'Changed files',
18 19 'Close': 'Close',
20 'Collapse all files': 'Collapse all files',
21 'Collapse {0} commit': 'Collapse {0} commit',
22 'Collapse {0} commits': 'Collapse {0} commits',
19 23 'Comment text will be set automatically based on currently selected status ({0}) ...': 'Comment text will be set automatically based on currently selected status ({0}) ...',
20 24 'Commit Authors are not allowed to be a reviewer.': 'Commit Authors are not allowed to be a reviewer.',
21 25 'Context file: ': 'Context file: ',
22 26 'Delete this comment?': 'Delete this comment?',
23 27 'Diff to Commit ': 'Diff to Commit ',
28 'Expand all files': 'Expand all files',
29 'Expand {0} commit': 'Expand {0} commit',
30 'Expand {0} commits': 'Expand {0} commits',
24 31 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.': 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.',
25 32 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.': 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.',
26 33 'Follow': 'Seguir',
34 'Force updating...': 'Force updating...',
27 35 'Hide full context diff': 'Hide full context diff',
28 36 'Hide whitespace changes': 'Hide whitespace changes',
29 37 'Invite reviewers to this discussion': 'Invite reviewers to this discussion',
30 38 'Leave a comment on line {0}.': 'Leave a comment on line {0}.',
31 39 'Leave a comment, or click resolve button to resolve TODO comment #{0}': 'Leave a comment, or click resolve button to resolve TODO comment #{0}',
40 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}': 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}',
32 41 'Lifetime': 'Tempo de Vida',
33 42 'Loading ...': 'Carregando...',
34 43 'Loading failed': 'Loading failed',
35 44 'Loading more results...': 'Loading more results...',
45 'Loading...': 'Loading...',
36 46 'No bookmarks available yet.': 'No bookmarks available yet.',
37 47 'No branches available yet.': 'No branches available yet.',
38 48 'No forks available yet.': 'No forks available yet.',
@@ -41,7 +51,9 b' var _TM = {'
41 51 'No matching files': 'Nenhum arquivo encontrado',
42 52 'No pull requests available yet.': 'No pull requests available yet.',
43 53 'No repositories available yet.': 'No repositories available yet.',
54 'No repositories present.': 'No repositories present.',
44 55 'No repository groups available yet.': 'No repository groups available yet.',
56 'No repository groups present.': 'No repository groups present.',
45 57 'No results': 'No results',
46 58 'No ssh keys available yet.': 'No ssh keys available yet.',
47 59 'No tags available yet.': 'No tags available yet.',
@@ -125,9 +137,11 b' var _TM = {'
125 137 '{0} min': '{0} min',
126 138 '{0} month': '{0} month',
127 139 '{0} months': '{0} months',
140 '{0} of {1} repositories': '{0} of {1} repositories',
128 141 '{0} of {1} repository groups': '{0} of {1} repository groups',
129 142 '{0} out of {1} ssh keys': '{0} out of {1} ssh keys',
130 143 '{0} out of {1} users': '{0} out of {1} users',
144 '{0} repositories': '{0} repositories',
131 145 '{0} repository groups': '{0} repository groups',
132 146 '{0} results are available, use up and down arrow keys to navigate.': '{0} results are available, use up and down arrow keys to navigate.',
133 147 '{0} sec': '{0} sec',
@@ -11,28 +11,38 b' var _TM = {'
11 11 'All Authors': 'All Authors',
12 12 'All individual reviewers must vote.': 'All individual reviewers must vote.',
13 13 'All reviewers must vote.': 'All reviewers must vote.',
14 'Are you sure to close this pull request without merging?': 'Are you sure to close this pull request without merging?',
14 15 'At least {0} reviewer must vote.': 'At least {0} reviewer must vote.',
15 16 'At least {0} reviewers must vote.': 'At least {0} reviewers must vote.',
16 17 'Author is not allowed to be a reviewer.': 'Author is not allowed to be a reviewer.',
17 18 'Changed files': 'Changed files',
18 19 'Close': 'Close',
20 'Collapse all files': 'Collapse all files',
21 'Collapse {0} commit': 'Collapse {0} commit',
22 'Collapse {0} commits': 'Collapse {0} commits',
19 23 'Comment text will be set automatically based on currently selected status ({0}) ...': 'Comment text will be set automatically based on currently selected status ({0}) ...',
20 24 'Commit Authors are not allowed to be a reviewer.': 'Commit Authors are not allowed to be a reviewer.',
21 25 'Context file: ': 'Context file: ',
22 26 'Delete this comment?': 'Delete this comment?',
23 27 'Diff to Commit ': 'Diff to Commit ',
28 'Expand all files': 'Expand all files',
29 'Expand {0} commit': 'Expand {0} commit',
30 'Expand {0} commits': 'Expand {0} commits',
24 31 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.': 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.',
25 32 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.': 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.',
26 33 'Follow': 'Наблюдать',
34 'Force updating...': 'Force updating...',
27 35 'Hide full context diff': 'Hide full context diff',
28 36 'Hide whitespace changes': 'Hide whitespace changes',
29 37 'Invite reviewers to this discussion': 'Invite reviewers to this discussion',
30 38 'Leave a comment on line {0}.': 'Leave a comment on line {0}.',
31 39 'Leave a comment, or click resolve button to resolve TODO comment #{0}': 'Leave a comment, or click resolve button to resolve TODO comment #{0}',
40 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}': 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}',
32 41 'Lifetime': 'Срок',
33 42 'Loading ...': 'Загрузка...',
34 43 'Loading failed': 'Loading failed',
35 44 'Loading more results...': 'Loading more results...',
45 'Loading...': 'Loading...',
36 46 'No bookmarks available yet.': 'No bookmarks available yet.',
37 47 'No branches available yet.': 'No branches available yet.',
38 48 'No forks available yet.': 'No forks available yet.',
@@ -41,7 +51,9 b' var _TM = {'
41 51 'No matching files': 'Нет совпадений',
42 52 'No pull requests available yet.': 'No pull requests available yet.',
43 53 'No repositories available yet.': 'No repositories available yet.',
54 'No repositories present.': 'No repositories present.',
44 55 'No repository groups available yet.': 'No repository groups available yet.',
56 'No repository groups present.': 'No repository groups present.',
45 57 'No results': 'No results',
46 58 'No ssh keys available yet.': 'No ssh keys available yet.',
47 59 'No tags available yet.': 'No tags available yet.',
@@ -125,9 +137,11 b' var _TM = {'
125 137 '{0} min': '{0} min',
126 138 '{0} month': '{0} month',
127 139 '{0} months': '{0} months',
140 '{0} of {1} repositories': '{0} of {1} repositories',
128 141 '{0} of {1} repository groups': '{0} of {1} repository groups',
129 142 '{0} out of {1} ssh keys': '{0} out of {1} ssh keys',
130 143 '{0} out of {1} users': '{0} out of {1} users',
144 '{0} repositories': '{0} repositories',
131 145 '{0} repository groups': '{0} repository groups',
132 146 '{0} results are available, use up and down arrow keys to navigate.': '{0} results are available, use up and down arrow keys to navigate.',
133 147 '{0} sec': '{0} sec',
@@ -11,28 +11,38 b' var _TM = {'
11 11 'All Authors': 'All Authors',
12 12 'All individual reviewers must vote.': 'All individual reviewers must vote.',
13 13 'All reviewers must vote.': 'All reviewers must vote.',
14 'Are you sure to close this pull request without merging?': 'Are you sure to close this pull request without merging?',
14 15 'At least {0} reviewer must vote.': 'At least {0} reviewer must vote.',
15 16 'At least {0} reviewers must vote.': 'At least {0} reviewers must vote.',
16 17 'Author is not allowed to be a reviewer.': 'Author is not allowed to be a reviewer.',
17 18 'Changed files': 'Changed files',
18 19 'Close': 'Close',
20 'Collapse all files': 'Collapse all files',
21 'Collapse {0} commit': 'Collapse {0} commit',
22 'Collapse {0} commits': 'Collapse {0} commits',
19 23 'Comment text will be set automatically based on currently selected status ({0}) ...': 'Comment text will be set automatically based on currently selected status ({0}) ...',
20 24 'Commit Authors are not allowed to be a reviewer.': 'Commit Authors are not allowed to be a reviewer.',
21 25 'Context file: ': 'Context file: ',
22 26 'Delete this comment?': 'Delete this comment?',
23 27 'Diff to Commit ': 'Diff to Commit ',
28 'Expand all files': 'Expand all files',
29 'Expand {0} commit': 'Expand {0} commit',
30 'Expand {0} commits': 'Expand {0} commits',
24 31 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.': 'Fetching repository state failed. Error code: {0} {1}. Try <a href="{2}">refreshing</a> this page.',
25 32 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.': 'Fetching repository state failed. Error code: {0} {1}. Try refreshing this page.',
26 33 'Follow': 'Follow',
34 'Force updating...': 'Force updating...',
27 35 'Hide full context diff': 'Hide full context diff',
28 36 'Hide whitespace changes': 'Hide whitespace changes',
29 37 'Invite reviewers to this discussion': 'Invite reviewers to this discussion',
30 38 'Leave a comment on line {0}.': 'Leave a comment on line {0}.',
31 39 'Leave a comment, or click resolve button to resolve TODO comment #{0}': 'Leave a comment, or click resolve button to resolve TODO comment #{0}',
40 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}': 'Leave a resolution comment, or click resolve button to resolve TODO comment #{0}',
32 41 'Lifetime': '终身',
33 42 'Loading ...': 'Loading ...',
34 43 'Loading failed': 'Loading failed',
35 44 'Loading more results...': 'Loading more results...',
45 'Loading...': 'Loading...',
36 46 'No bookmarks available yet.': 'No bookmarks available yet.',
37 47 'No branches available yet.': 'No branches available yet.',
38 48 'No forks available yet.': 'No forks available yet.',
@@ -41,7 +51,9 b' var _TM = {'
41 51 'No matching files': 'No matching files',
42 52 'No pull requests available yet.': 'No pull requests available yet.',
43 53 'No repositories available yet.': 'No repositories available yet.',
54 'No repositories present.': 'No repositories present.',
44 55 'No repository groups available yet.': 'No repository groups available yet.',
56 'No repository groups present.': 'No repository groups present.',
45 57 'No results': 'No results',
46 58 'No ssh keys available yet.': 'No ssh keys available yet.',
47 59 'No tags available yet.': 'No tags available yet.',
@@ -125,9 +137,11 b' var _TM = {'
125 137 '{0} min': '{0} min',
126 138 '{0} month': '{0} month',
127 139 '{0} months': '{0} months',
140 '{0} of {1} repositories': '{0} of {1} repositories',
128 141 '{0} of {1} repository groups': '{0} of {1} repository groups',
129 142 '{0} out of {1} ssh keys': '{0} out of {1} ssh keys',
130 143 '{0} out of {1} users': '{0} out of {1} users',
144 '{0} repositories': '{0} repositories',
131 145 '{0} repository groups': '{0} repository groups',
132 146 '{0} results are available, use up and down arrow keys to navigate.': '{0} results are available, use up and down arrow keys to navigate.',
133 147 '{0} sec': '{0} sec',
@@ -31,6 +31,7 b' function registerRCRoutes() {'
31 31 pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
32 32 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
33 33 pyroutes.register('hovercard_user', '/_hovercard/user/%(user_id)s', ['user_id']);
34 pyroutes.register('hovercard_username', '/_hovercard/username/%(username)s', ['username']);
34 35 pyroutes.register('hovercard_user_group', '/_hovercard/user_group/%(user_group_id)s', ['user_group_id']);
35 36 pyroutes.register('hovercard_pull_request', '/_hovercard/pull_request/%(pull_request_id)s', ['pull_request_id']);
36 37 pyroutes.register('hovercard_repo_commit', '/_hovercard/commit/%(repo_name)s/%(commit_id)s', ['repo_name', 'commit_id']);
@@ -299,6 +299,10 b' var tooltipActivate = function () {'
299 299 var altHovercard =$origin.data('hovercardAlt');
300 300
301 301 if (hovercardUrl !== undefined && hovercardUrl !== "") {
302 if (hovercardUrl.substr(0,12) === 'pyroutes.url'){
303 hovercardUrl = eval(hovercardUrl)
304 }
305
302 306 var loaded = loadHoverCard(hovercardUrl, altHovercard, function (data) {
303 307 instance.content(data);
304 308 })
@@ -102,7 +102,7 b''
102 102 { data: {"_": "author",
103 103 "sort": "author_raw"}, title: "${_("Author")}", width: "250px", className: "td-user" },
104 104 { data: {"_": "type",
105 "sort": "type"}, title: "${_("Type")}", width: "70px", className: "td-tags" },
105 "sort": "type"}, title: "${_("Type")}", width: "100px", className: "td-gist-type" },
106 106 { data: {"_": "access_id",
107 107 "sort": "access_id"}, title: "${_("Name")}", width:"150px", className: "td-componentname" },
108 108 { data: {"_": "description",
@@ -110,7 +110,7 b''
110 110 { data: {"_": "created_on",
111 111 "sort": "created_on_raw"}, title: "${_("Created on")}", className: "td-time" },
112 112 { data: {"_": "expires",
113 "sort": "expires"}, title: "${_("Expires")}", className: "td-exp" }
113 "sort": "expires"}, title: "${_("Expires")}", width: "200px", className: "td-expire" }
114 114 ],
115 115 language: {
116 116 paginate: DEFAULT_GRID_PAGINATION,
@@ -71,7 +71,7 b''
71 71 <div class="input">
72 72 ${h.text('description', class_='medium', placeholder=_('Description'))}
73 73 ${h.hidden('lifetime')}
74 ${h.select('role', '', c.role_options)}
74 ${h.select('role', request.GET.get('token_role', ''), c.role_options)}
75 75
76 76 % if c.allow_scoped_tokens:
77 77 ${h.hidden('scope_repo_id')}
@@ -4,7 +4,9 b''
4 4
5 5 % if c.extern_type != 'rhodecode':
6 6 <p>${_('Your user account details are managed by an external source. Details cannot be managed here.')}
7 <br/>${_('Source type')}: <strong>${c.extern_type}</strong>
7 <br/>${_('For VCS access please generate')}
8 <a href="${h.route_path('my_account_auth_tokens', _query={'token_role':'token_role_vcs'})}">Authentication Token</a> or <a href="${h.route_path('my_account_ssh_keys_generate')}">SSH Key</a>.
9 <br/>${_('Source type')}: <strong>${c.extern_type}</strong>
8 10 </p>
9 11 % else:
10 12 ${c.form.render() | n}
@@ -8,6 +8,17 b''
8 8 </div>
9 9
10 10 <div class="panel-body fields">
11 %if c.extern_type != 'rhodecode':
12 <% readonly = "readonly" %>
13 <% disabled = " disabled" %>
14 <div class="alert-warning" style="margin:0px 0px 20px 0px; padding: 10px">
15 <strong>${_('This user was created from external source (%s). Editing some of the settings is limited.' % c.extern_type)}</strong>
16 </div>
17 <div style="margin:-10px 0px 20px 0px;">
18 ${_('For VCS access please generate')}
19 <a href="${h.route_path('my_account_auth_tokens', _query={'token_role':'token_role_vcs'})}">Authentication Token</a> or <a href="${h.route_path('my_account_ssh_keys_generate')}">SSH Key</a>.
20 </div>
21 %endif
11 22 <div class="field">
12 23 <div class="label">
13 24 ${_('Photo')}:
@@ -11,13 +11,19 b''
11 11
12 12 %if c.extern_type != 'rhodecode':
13 13 <% readonly = "readonly" %>
14 <% disabled = "disabled" %>
14 <% disabled = " disabled" %>
15 <div class="alert-warning" style="margin:0px 0px 20px 0px; padding: 10px">
16 <strong>${_('This user was created from external source (%s). Editing some of the settings is limited.' % c.extern_type)}</strong>
17 </div>
18 <div style="margin:-10px 0px 20px 0px;">
19 ${_('For VCS access please generate')}
20 <a href="${h.route_path('my_account_auth_tokens', _query={'token_role':'token_role_vcs'})}">Authentication Token</a> or <a href="${h.route_path('my_account_ssh_keys_generate')}">SSH Key</a>.
21 </div>
22 %endif
23
24 %if c.extern_type != 'rhodecode':
15 25 <div class="infoform">
16 26 <div class="fields">
17 <p>${_('Your user account details are managed by an external source. Details cannot be managed here.')}
18 <br/>${_('Source type')}: <strong>${c.extern_type}</strong>
19 </p>
20
21 27 <div class="field">
22 28 <div class="label">
23 29 <label for="username">${_('Username')}:</label>
@@ -55,7 +55,13 b''
55 55 <div class="textarea editor">
56 56 ${h.textarea('group_description',cols=23,rows=5,class_="medium")}
57 57 <% metatags_url = h.literal('''<a href="#metatagsShow" onclick="$('#meta-tags-desc').toggle();return false">meta-tags</a>''') %>
58 <span class="help-block">${_('Plain text format with support of {metatags}').format(metatags=metatags_url)|n}</span>
58 <span class="help-block">
59 % if c.visual.stylify_metatags:
60 ${_('Plain text format with {metatags} support.').format(metatags=metatags_url)|n}
61 % else:
62 ${_('Plain text format.')}
63 % endif
64 </span>
59 65 <span id="meta-tags-desc" style="display: none">
60 66 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
61 67 ${dt.metatags_help()}
@@ -28,7 +28,7 b''
28 28 <div class="sidebar">
29 29 <ul class="nav nav-pills nav-stacked">
30 30 <li class="${h.is_active('settings', c.active)}"><a href="${h.route_path('edit_repo_group', repo_group_name=c.repo_group.group_name)}">${_('Settings')}</a></li>
31 <li class="${h.is_active('permissions', c.active)}"><a href="${h.route_path('edit_repo_group_perms', repo_group_name=c.repo_group.group_name)}">${_('Permissions')}</a></li>
31 <li class="${h.is_active('permissions', c.active)}"><a href="${h.route_path('edit_repo_group_perms', repo_group_name=c.repo_group.group_name)}">${_('Access Permissions')}</a></li>
32 32 <li class="${h.is_active('advanced', c.active)}"><a href="${h.route_path('edit_repo_group_advanced', repo_group_name=c.repo_group.group_name)}">${_('Advanced')}</a></li>
33 33 <li class="${h.is_active('integrations', c.active)}"><a href="${h.route_path('repo_group_integrations_home', repo_group_name=c.repo_group.group_name)}">${_('Integrations')}</a></li>
34 34 </ul>
@@ -61,7 +61,12 b''
61 61 ${base.gravatar(_user.email, 16, user=_user, tooltip=True)}
62 62 <span class="user">
63 63 % if _user.username == h.DEFAULT_USER:
64 ${h.DEFAULT_USER} <span class="user-perm-help-text"> - ${_('permission for all other users')}</span>
64 ${h.DEFAULT_USER}
65 % if _user.active:
66 <span class="user-perm-help-text"> - ${_('permission for other logged in and anonymous users')}</span>
67 % else:
68 <span class="user-perm-help-text"> - ${_('permission for other logged in users')}</span>
69 % endif
65 70 % else:
66 71 ${h.link_to_user(_user.username)}
67 72 %if getattr(_user, 'duplicate_perm', None):
@@ -104,7 +109,12 b''
104 109 ${base.gravatar(_user.email, 16, user=_user, tooltip=True)}
105 110 <span class="user">
106 111 % if _user.username == h.DEFAULT_USER:
107 ${h.DEFAULT_USER} <span class="user-perm-help-text"> - ${_('permission for all other users')}</span>
112 ${h.DEFAULT_USER}
113 % if _user.active:
114 <span class="user-perm-help-text"> - ${_('permission for other logged in and anonymous users')}</span>
115 % else:
116 <span class="user-perm-help-text"> - ${_('permission for other logged in users')}</span>
117 % endif
108 118 % else:
109 119 ${h.link_to_user(_user.username)}
110 120 %if getattr(_user, 'duplicate_perm', None):
@@ -59,7 +59,13 b''
59 59 ${c.form.render_error(request, c.form['repo_group_description'])|n}
60 60
61 61 <% metatags_url = h.literal('''<a href="#metatagsShow" onclick="$('#meta-tags-desc').toggle();return false">meta-tags</a>''') %>
62 <span class="help-block">${_('Plain text format with support of {metatags}').format(metatags=metatags_url)|n}</span>
62 <span class="help-block">
63 % if c.visual.stylify_metatags:
64 ${_('Plain text format with {metatags} support.').format(metatags=metatags_url)|n}
65 % else:
66 ${_('Plain text format.')}
67 % endif
68 </span>
63 69 <span id="meta-tags-desc" style="display: none">
64 70 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
65 71 ${dt.metatags_help()}
@@ -68,7 +68,14 b''
68 68 <div class="textarea editor">
69 69 ${h.textarea('repo_description',cols=23,rows=5,class_="medium")}
70 70 <% metatags_url = h.literal('''<a href="#metatagsShow" onclick="$('#meta-tags-desc').toggle();return false">meta-tags</a>''') %>
71 <span class="help-block">${_('Plain text format with support of {metatags}. Add a README file for longer descriptions').format(metatags=metatags_url)|n}</span>
71 <span class="help-block">
72 % if c.visual.stylify_metatags:
73 ${_('Plain text format with {metatags} support.').format(metatags=metatags_url)|n}
74 % else:
75 ${_('Plain text format.')}
76 % endif
77 ${_('Add a README file for longer descriptions')}
78 </span>
72 79 <span id="meta-tags-desc" style="display: none">
73 80 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
74 81 ${dt.metatags_help()}
@@ -23,43 +23,48 b''
23 23 <div class="panel-heading">
24 24 <h3 class="panel-title">${_('Inherited Issue Tracker Patterns')}</h3>
25 25 </div>
26
26 27 <div class="panel-body">
27 <table class="rctable issuetracker readonly">
28 <tr>
29 <th>${_('Description')}</th>
30 <th>${_('Pattern')}</th>
31 <th>${_('Url')}</th>
32 <th>${_('Prefix')}</th>
33 <th ></th>
34 </tr>
35 %for uid, entry in c.global_patterns.items():
36 <tr id="${uid}">
37 <td class="td-description issuetracker_desc">
38 <span class="entry">
39 ${entry.desc}
40 </span>
41 </td>
42 <td class="td-regex issuetracker_pat">
43 <span class="entry">
44 ${entry.pat}
45 </span>
46 </td>
47 <td class="td-url issuetracker_url">
48 <span class="entry">
49 ${entry.url}
50 </span>
51 </td>
52 <td class="td-prefix issuetracker_pref">
53 <span class="entry">
54 ${entry.pref}
55 </span>
56 </td>
57 <td class="td-action">
58 </td>
59 </tr>
60 %endfor
28 <table class="rctable issuetracker readonly">
29 <tr>
30 <th>${_('Description')}</th>
31 <th>${_('Pattern')}</th>
32 <th>${_('Url')}</th>
33 <th>${_('Prefix')}</th>
34 <th></th>
35 </tr>
61 36
62 </table>
37 % for uid, entry in c.global_patterns.items():
38 <tr id="${uid}">
39 <td class="td-description issuetracker_desc">
40 <span class="entry">
41 ${entry.desc}
42 </span>
43 </td>
44 <td class="td-regex issuetracker_pat">
45 <span class="entry">
46 ${entry.pat}
47 </span>
48 </td>
49 <td class="td-url issuetracker_url">
50 <span class="entry">
51 ${entry.url}
52 </span>
53 </td>
54 <td class="td-prefix issuetracker_pref">
55 <span class="entry">
56 ${entry.pref}
57 </span>
58 </td>
59 <td class="td-action">
60 </td>
61 </tr>
62 % endfor
63
64 </table>
65 <div class="buttons">
66 <button type="submit" class="btn btn-primary save-inheritance" id="save">${_('Save')}</button>
67 </div>
63 68 </div>
64 69 </div>
65 70 </div>
@@ -77,7 +82,6 b''
77 82 )}
78 83 <div class="buttons">
79 84 <button type="submit" class="btn btn-primary save-inheritance" id="save">${_('Save')}</button>
80 <button type="reset" class="btn reset-inheritance">${_('Reset')}</button>
81 85 </div>
82 86 </div>
83 87 </div>
@@ -58,7 +58,11 b''
58 58 <td class="private_repo_msg">
59 59 ${base.gravatar(h.DEFAULT_USER_EMAIL, 16)}
60 60 ${h.DEFAULT_USER} - ${_('only users/user groups explicitly added here will have access')}</td>
61 <td></td>
61 <td class="td-action">
62 <span class="tooltip btn btn-link btn-default" onclick="setPrivateRepo(false); return false" title="${_('Private repositories are only visible to people explicitly added as collaborators. Default permissions wont apply')}">
63 ${_('un-set private mode')}
64 </span>
65 </td>
62 66 <td class="quick_repo_menu">
63 67 % if c.rhodecode_user.is_admin:
64 68 <i class="icon-more"></i>
@@ -83,7 +87,12 b''
83 87 ${base.gravatar(_user.email, 16, user=_user, tooltip=True)}
84 88 <span class="user">
85 89 % if _user.username == h.DEFAULT_USER:
86 ${h.DEFAULT_USER} <span class="user-perm-help-text"> - ${_('permission for all other users')}</span>
90 ${h.DEFAULT_USER}
91 % if _user.active:
92 <span class="user-perm-help-text"> - ${_('permission for other logged in and anonymous users')}</span>
93 % else:
94 <span class="user-perm-help-text"> - ${_('permission for other logged in users')}</span>
95 % endif
87 96 % else:
88 97 ${h.link_to_user(_user.username)}
89 98 %if getattr(_user, 'duplicate_perm', None):
@@ -106,7 +115,7 b''
106 115 ${_('Remove')}
107 116 </span>
108 117 %elif _user.username == h.DEFAULT_USER:
109 <span class="tooltip btn btn-link btn-default" onclick="enablePrivateRepo(); return false" title="${_('Private repositories are only visible to people explicitly added as collaborators.')}">
118 <span class="tooltip btn btn-link btn-default" onclick="setPrivateRepo(true); return false" title="${_('Private repositories are only visible to people explicitly added as collaborators. Default permissions wont apply')}">
110 119 ${_('set private mode')}
111 120 </span>
112 121 %endif
@@ -204,9 +213,10 b''
204 213 });
205 214 quick_repo_menu();
206 215
207 var enablePrivateRepo = function () {
216 var setPrivateRepo = function (private) {
208 217 var postData = {
209 'csrf_token': CSRF_TOKEN
218 'csrf_token': CSRF_TOKEN,
219 'private': private
210 220 };
211 221
212 222 var success = function(o) {
@@ -174,7 +174,14 b''
174 174 ${c.form.render_error(request, c.form['repo_description'])|n}
175 175
176 176 <% metatags_url = h.literal('''<a href="#metatagsShow" onclick="$('#meta-tags-desc').toggle();return false">meta-tags</a>''') %>
177 <span class="help-block">${_('Plain text format with support of {metatags}. Add a README file for longer descriptions').format(metatags=metatags_url)|n}</span>
177 <span class="help-block">
178 % if c.visual.stylify_metatags:
179 ${_('Plain text format with {metatags} support.').format(metatags=metatags_url)|n}
180 % else:
181 ${_('Plain text format.')}
182 % endif
183 ${_('Add a README file for longer descriptions')}
184 </span>
178 185 <span id="meta-tags-desc" style="display: none">
179 186 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
180 187 ${dt.metatags_help()}
@@ -1,5 +1,5 b''
1 1
2 <div id="update_notice" style="display: none; margin: -40px 0px 20px 0px">
2 <div id="update_notice" style="display: none; margin: 0px 0px 30px 0px">
3 3 <div>${_('Checking for updates...')}</div>
4 4 </div>
5 5
@@ -18,7 +18,11 b''
18 18
19 19 <div class="panel panel-default">
20 20 <div class="panel-heading">
21 <h3 class="panel-title">${_('User Group: %s') % c.user_group.users_group_name}</h3>
21 <h3 class="panel-title">
22 <i class="icon-user-group" title="${_('User group')}"></i>
23 ${h.link_to_group(c.user_group.users_group_name)}
24 - ${_('Advanced')}
25 </h3>
22 26 </div>
23 27 <div class="panel-body">
24 28 ${base.dt_info_panel(elems)}
@@ -2,7 +2,11 b''
2 2
3 3 <div class="panel panel-default">
4 4 <div class="panel-heading">
5 <h3 class="panel-title">${_('User Group Permissions')}</h3>
5 <h3 class="panel-title">
6 <i class="icon-user-group" title="${_('User group')}"></i>
7 ${h.link_to_group(c.user_group.users_group_name)}
8 - ${_('Access Permissions')}
9 </h3>
6 10 </div>
7 11 <div class="panel-body">
8 12 ${h.secure_form(h.route_path('edit_user_group_perms_update', user_group_id=c.user_group.users_group_id), request=request)}
@@ -63,7 +67,12 b''
63 67 ${base.gravatar(_user.email, 16, user=_user, tooltip=True)}
64 68 <span class="user">
65 69 % if _user.username == h.DEFAULT_USER:
66 ${h.DEFAULT_USER} <span class="user-perm-help-text"> - ${_('permission for all other users')}</span>
70 ${h.DEFAULT_USER}
71 % if _user.active:
72 <span class="user-perm-help-text"> - ${_('permission for other logged in and anonymous users')}</span>
73 % else:
74 <span class="user-perm-help-text"> - ${_('permission for other logged in users')}</span>
75 % endif
67 76 % else:
68 77 ${h.link_to_user(_user.username)}
69 78 %if getattr(_user, 'duplicate_perm', None):
@@ -106,7 +115,12 b''
106 115 ${base.gravatar(_user.email, 16, user=_user, tooltip=True)}
107 116 <span class="user">
108 117 % if _user.username == h.DEFAULT_USER:
109 ${h.DEFAULT_USER} <span class="user-perm-help-text"> - ${_('permission for all other users')}</span>
118 ${h.DEFAULT_USER}
119 % if _user.active:
120 <span class="user-perm-help-text"> - ${_('permission for other logged in and anonymous users')}</span>
121 % else:
122 <span class="user-perm-help-text"> - ${_('permission for other logged in users')}</span>
123 % endif
110 124 % else:
111 125 ${h.link_to_user(_user.username)}
112 126 %if getattr(_user, 'duplicate_perm', None):
@@ -3,7 +3,11 b''
3 3
4 4 <div class="panel panel-default">
5 5 <div class="panel-heading">
6 <h3 class="panel-title">${_('User Group: %s') % c.user_group.users_group_name}</h3>
6 <h3 class="panel-title">
7 <i class="icon-user-group" title="${_('User group')}"></i>
8 ${h.link_to_group(c.user_group.users_group_name)}
9 - ${_('Settings')}
10 </h3>
7 11 </div>
8 12 <div class="panel-body">
9 13 ${h.secure_form(h.route_path('user_groups_update', user_group_id=c.user_group.users_group_id), id='edit_user_group', request=request)}
@@ -25,7 +25,10 b''
25 25
26 26 <div class="panel panel-default">
27 27 <div class="panel-heading">
28 <h3 class="panel-title">${_('User: {}').format(c.user.username)}</h3>
28 <h3 class="panel-title">
29 ${base.gravatar_with_user(c.user.username, 16, tooltip=False, _class='pull-left')}
30 &nbsp;- ${_('Access Permissions')}
31 </h3>
29 32 </div>
30 33 <div class="panel-body">
31 34 <table class="rctable">
@@ -4,8 +4,10 b''
4 4
5 5 <div class="panel panel-default">
6 6 <div class="panel-heading">
7 <h3 class="panel-title">${_('User Audit Logs')} -
8 ${_ungettext('%s entry', '%s entries', c.audit_logs.item_count) % (c.audit_logs.item_count)}
7 <h3 class="panel-title">
8 ${base.gravatar_with_user(c.user.username, 16, tooltip=False, _class='pull-left')}
9 &nbsp;- ${_('Audit Logs')}
10 (${_ungettext('%s entry', '%s entries', c.audit_logs.item_count) % (c.audit_logs.item_count)})
9 11 </h3>
10 12 <a href="${h.route_path('edit_user_audit_logs_download', user_id=c.user.user_id)}" class="panel-edit">${_('Download as JSON')}</a>
11 13 </div>
@@ -1,6 +1,11 b''
1 <%namespace name="base" file="/base/base.mako"/>
2
1 3 <div class="panel panel-default">
2 4 <div class="panel-heading">
3 <h3 class="panel-title">${_('Authentication Tokens')}</h3>
5 <h3 class="panel-title">
6 ${base.gravatar_with_user(c.user.username, 16, tooltip=False, _class='pull-left')}
7 &nbsp;- ${_('Authentication Tokens')}
8 </h3>
4 9 </div>
5 10 <div class="panel-body">
6 11 <div class="apikeys_wrap">
@@ -2,7 +2,10 b''
2 2
3 3 <div class="panel panel-default">
4 4 <div class="panel-heading">
5 <h3 class="panel-title">${_('Caches')}</h3>
5 <h3 class="panel-title">
6 ${base.gravatar_with_user(c.user.username, 16, tooltip=False, _class='pull-left')}
7 &nbsp;- ${_('Caches')}
8 </h3>
6 9 </div>
7 10 <div class="panel-body">
8 11 <p>
@@ -2,7 +2,10 b''
2 2
3 3 <div class="panel panel-default">
4 4 <div class="panel-heading">
5 <h3 class="panel-title">${_('Additional Email Addresses')}</h3>
5 <h3 class="panel-title">
6 ${base.gravatar_with_user(c.user.username, 16, tooltip=False, _class='pull-left')}
7 &nbsp;- ${_('Additional Email Addresses')}
8 </h3>
6 9 </div>
7 10 <div class="panel-body">
8 11 <div class="emails_wrap">
@@ -1,9 +1,12 b''
1 1 ## -*- coding: utf-8 -*-
2
2 <%namespace name="base" file="/base/base.mako"/>
3 3
4 4 <div class="panel panel-default">
5 5 <div class="panel-heading">
6 <h3 class="panel-title">${_('User groups administration')}</h3>
6 <h3 class="panel-title">
7 ${base.gravatar_with_user(c.user.username, 16, tooltip=False, _class='pull-left')}
8 &nbsp;- ${_('User groups administration')}
9 </h3>
7 10 </div>
8 11 <div class="panel-body">
9 12 <div class="fields">
@@ -1,6 +1,11 b''
1 <%namespace name="base" file="/base/base.mako"/>
2
1 3 <div class="panel panel-default">
2 4 <div class="panel-heading">
3 <h3 class="panel-title">${_('Custom IP Whitelist')}</h3>
5 <h3 class="panel-title">
6 ${base.gravatar_with_user(c.user.username, 16, tooltip=False, _class='pull-left')}
7 &nbsp;- ${_('Custom IP Whitelist')}
8 </h3>
4 9 </div>
5 10 <div class="panel-body">
6 11 <div class="ips_wrap">
@@ -2,7 +2,10 b''
2 2
3 3 <div class="panel panel-default user-profile">
4 4 <div class="panel-heading">
5 <h3 class="panel-title">${_('User Profile')}</h3>
5 <h3 class="panel-title">
6 ${base.gravatar_with_user(c.user.username, 16, tooltip=False, _class='pull-left')}
7 &nbsp;- ${_('User Profile')}
8 </h3>
6 9 </div>
7 10 <div class="panel-body">
8 11 <div class="user-profile-content">
@@ -73,7 +76,13 b''
73 76 <div class="input textarea editor">
74 77 ${h.textarea('description', rows=10, class_="medium")}
75 78 <% metatags_url = h.literal('''<a href="#metatagsShow" onclick="$('#meta-tags-desc').toggle();return false">meta-tags</a>''') %>
76 <span class="help-block">${_('Plain text format with support of {metatags}. Add a README file for longer descriptions').format(metatags=metatags_url)|n}</span>
79 <span class="help-block">
80 % if c.visual.stylify_metatags:
81 ${_('Plain text format with {metatags} support.').format(metatags=metatags_url)|n}
82 % else:
83 ${_('Plain text format.')}
84 % endif
85 </span>
77 86 <span id="meta-tags-desc" style="display: none">
78 87 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
79 88 ${dt.metatags_help()}
@@ -1,6 +1,11 b''
1 <%namespace name="base" file="/base/base.mako"/>
2
1 3 <div class="panel panel-default">
2 4 <div class="panel-heading">
3 <h3 class="panel-title">${_('SSH Keys')}</h3>
5 <h3 class="panel-title">
6 ${base.gravatar_with_user(c.user.username, 16, tooltip=False, _class='pull-left')}
7 &nbsp;- ${_('SSH Keys')}
8 </h3>
4 9 </div>
5 10 <div class="panel-body">
6 11 <div class="sshkeys_wrap">
@@ -1,11 +1,16 b''
1 <%namespace name="base" file="/base/base.mako"/>
2
1 3 <div class="panel panel-default">
2 4 <div class="panel-heading">
3 <h3 class="panel-title">${_('New SSH Key generation')}</h3>
5 <h3 class="panel-title">
6 ${base.gravatar_with_user(c.user.username, 16, tooltip=False, _class='pull-left')}
7 &nbsp;- ${_('New SSH Key generation')}
8 </h3>
4 9 </div>
5 10 <div class="panel-body">
6 11 %if c.ssh_enabled and c.ssh_key_generator_enabled:
7 12 <p>
8 ${_('Below is a 2048 bit generated SSH RSA key.')}<br/>
13 ${_('Below is a 2048 bit generated SSH RSA key.')}<br/>${_('If you use older systems please try to generate a')} <a href="${h.current_route_path(request, private_format='legacy')}">${_('legacy format')}</a> ssh key.<br/>
9 14 ${_('If You wish to use it to access RhodeCode via the SSH please save the private key and click `Use this generated key` at the bottom.')}
10 15 </p>
11 16 <h4>${_('Private key')}</h4>
@@ -230,13 +230,13 b''
230 230 </%def>
231 231
232 232
233 <%def name="gravatar_with_user(contact, size=16, show_disabled=False, tooltip=False)">
233 <%def name="gravatar_with_user(contact, size=16, show_disabled=False, tooltip=False, _class='rc-user')">
234 234 <%
235 235 email = h.email_or_none(contact)
236 236 rc_user = h.discover_user(contact)
237 237 %>
238 238
239 <div class="rc-user">
239 <div class="${_class}">
240 240 ${self.gravatar(email, size, tooltip=tooltip, tooltip_alt=contact, user=rc_user)}
241 241 <span class="${('user user-disabled' if show_disabled else 'user')}"> ${h.link_to_user(rc_user or contact)}</span>
242 242 </div>
@@ -405,10 +405,6 b''
405 405 %endif
406 406 %endif
407 407 </ul>
408 % else:
409 <a class="menulink disabled">
410 <div class="menulabel">${_('Options')}<div class="show_more"></div></div>
411 </a>
412 408 % endif
413 409 </li>
414 410
@@ -59,14 +59,14 b' examples = ['
59 59
60 60 (
61 61 'Pivotal Tracker',
62 '(?:pivot-)(?<project_id>\d+)-(?<story>\d+)',
62 '(?:pivot-)(?P<project_id>\d+)-(?P<story>\d+)',
63 63 'https://www.pivotaltracker.com/s/projects/${project_id}/stories/${story}',
64 64 'PIV-',
65 65 ),
66 66
67 67 (
68 68 'Trello',
69 '(?:trello-)(?<card_id>[a-zA-Z0-9]+)',
69 '(?:trello-)(?P<card_id>[a-zA-Z0-9]+)',
70 70 'https://trello.com/example.com/${card_id}',
71 71 'TRELLO-',
72 72 ),
@@ -5,7 +5,7 b''
5 5 ##
6 6 <%namespace name="base" file="/base/base.mako"/>
7 7
8 <%def name="comment_block(comment, inline=False)">
8 <%def name="comment_block(comment, inline=False, active_pattern_entries=None)">
9 9 <% pr_index_ver = comment.get_index_version(getattr(c, 'versions', [])) %>
10 10 <% latest_ver = len(getattr(c, 'versions', [])) %>
11 11 % if inline:
@@ -156,7 +156,7 b''
156 156 </div>
157 157 </div>
158 158 <div class="text">
159 ${h.render(comment.text, renderer=comment.renderer, mentions=True, repo_name=getattr(c, 'repo_name', None))}
159 ${h.render(comment.text, renderer=comment.renderer, mentions=True, repo_name=getattr(c, 'repo_name', None), active_pattern_entries=active_pattern_entries)}
160 160 </div>
161 161
162 162 </div>
@@ -164,13 +164,17 b''
164 164
165 165 ## generate main comments
166 166 <%def name="generate_comments(comments, include_pull_request=False, is_pull_request=False)">
167 <%
168 active_pattern_entries = h.get_active_pattern_entries(getattr(c, 'repo_name', None))
169 %>
170
167 171 <div class="general-comments" id="comments">
168 172 %for comment in comments:
169 173 <div id="comment-tr-${comment.comment_id}">
170 174 ## only render comments that are not from pull request, or from
171 175 ## pull request and a status change
172 176 %if not comment.pull_request or (comment.pull_request and comment.status_change) or include_pull_request:
173 ${comment_block(comment)}
177 ${comment_block(comment, active_pattern_entries=active_pattern_entries)}
174 178 %endif
175 179 </div>
176 180 %endfor
@@ -60,12 +60,16 b" return '%s_%s_%i' % (h.md5_safe(commit+f"
60 60 <%
61 61 diffset_container_id = h.md5(diffset.target_ref)
62 62 collapse_all = len(diffset.files) > collapse_when_files_over
63 active_pattern_entries = h.get_active_pattern_entries(getattr(c, 'repo_name', None))
63 64 %>
64 65
65 66 %if use_comments:
67
68 ## Template for injecting comments
66 69 <div id="cb-comments-inline-container-template" class="js-template">
67 ${inline_comments_container([], inline_comments)}
70 ${inline_comments_container([])}
68 71 </div>
72
69 73 <div class="js-template" id="cb-comment-inline-form-template">
70 74 <div class="comment-inline-form ac">
71 75
@@ -259,7 +263,7 b" return '%s_%s_%i' % (h.md5_safe(commit+f"
259 263 ## new/deleted/empty content case
260 264 % if not filediff.hunks:
261 265 ## Comment container, on "fakes" hunk that contains all data to render comments
262 ${render_hunk_lines(filediff, c.user_session_attrs["diffmode"], filediff.hunk_ops, use_comments=use_comments, inline_comments=inline_comments)}
266 ${render_hunk_lines(filediff, c.user_session_attrs["diffmode"], filediff.hunk_ops, use_comments=use_comments, inline_comments=inline_comments, active_pattern_entries=active_pattern_entries)}
263 267 % endif
264 268
265 269 %if filediff.limited_diff:
@@ -299,7 +303,7 b" return '%s_%s_%i' % (h.md5_safe(commit+f"
299 303 ${hunk.section_header}
300 304 </td>
301 305 </tr>
302 ${render_hunk_lines(filediff, c.user_session_attrs["diffmode"], hunk, use_comments=use_comments, inline_comments=inline_comments)}
306 ${render_hunk_lines(filediff, c.user_session_attrs["diffmode"], hunk, use_comments=use_comments, inline_comments=inline_comments, active_pattern_entries=active_pattern_entries)}
303 307 % endfor
304 308
305 309 <% unmatched_comments = (inline_comments or {}).get(filediff.patch['filename'], {}) %>
@@ -323,7 +327,7 b" return '%s_%s_%i' % (h.md5_safe(commit+f"
323 327 <td class="cb-lineno cb-context"></td>
324 328 <td class="cb-lineno cb-context"></td>
325 329 <td class="cb-content cb-context">
326 ${inline_comments_container(comments, inline_comments)}
330 ${inline_comments_container(comments, active_pattern_entries=active_pattern_entries)}
327 331 </td>
328 332 </tr>
329 333 %elif c.user_session_attrs["diffmode"] == 'sideside':
@@ -348,7 +352,7 b" return '%s_%s_%i' % (h.md5_safe(commit+f"
348 352 <td class="cb-lineno cb-context"></td>
349 353 <td class="cb-content cb-context">
350 354 % if lineno.startswith('o'):
351 ${inline_comments_container(comments, inline_comments)}
355 ${inline_comments_container(comments, active_pattern_entries=active_pattern_entries)}
352 356 % endif
353 357 </td>
354 358
@@ -356,7 +360,7 b" return '%s_%s_%i' % (h.md5_safe(commit+f"
356 360 <td class="cb-lineno cb-context"></td>
357 361 <td class="cb-content cb-context">
358 362 % if lineno.startswith('n'):
359 ${inline_comments_container(comments, inline_comments)}
363 ${inline_comments_container(comments, active_pattern_entries=active_pattern_entries)}
360 364 % endif
361 365 </td>
362 366 </tr>
@@ -415,7 +419,7 b" return '%s_%s_%i' % (h.md5_safe(commit+f"
415 419 <td class="cb-lineno cb-context"></td>
416 420 <td class="cb-lineno cb-context"></td>
417 421 <td class="cb-content cb-context">
418 ${inline_comments_container(comments_dict['comments'], inline_comments)}
422 ${inline_comments_container(comments_dict['comments'], active_pattern_entries=active_pattern_entries)}
419 423 </td>
420 424 </tr>
421 425 %elif c.user_session_attrs["diffmode"] == 'sideside':
@@ -427,7 +431,7 b" return '%s_%s_%i' % (h.md5_safe(commit+f"
427 431 <td class="cb-data cb-context"></td>
428 432 <td class="cb-lineno cb-context"></td>
429 433 <td class="cb-content cb-context">
430 ${inline_comments_container(comments_dict['comments'], inline_comments)}
434 ${inline_comments_container(comments_dict['comments'], active_pattern_entries=active_pattern_entries)}
431 435 </td>
432 436 </tr>
433 437 %endif
@@ -584,10 +588,11 b' from rhodecode.lib.diffs import NEW_FILE'
584 588 </%def>
585 589
586 590
587 <%def name="inline_comments_container(comments, inline_comments)">
591 <%def name="inline_comments_container(comments, active_pattern_entries=None)">
592
588 593 <div class="inline-comments">
589 594 %for comment in comments:
590 ${commentblock.comment_block(comment, inline=True)}
595 ${commentblock.comment_block(comment, inline=True, active_pattern_entries=active_pattern_entries)}
591 596 %endfor
592 597 % if comments and comments[-1].outdated:
593 598 <span class="btn btn-secondary cb-comment-add-button comment-outdated}" style="display: none;}">
@@ -619,7 +624,7 b' def get_comments_for(diff_type, comments'
619 624 return data
620 625 %>
621 626
622 <%def name="render_hunk_lines_sideside(filediff, hunk, use_comments=False, inline_comments=None)">
627 <%def name="render_hunk_lines_sideside(filediff, hunk, use_comments=False, inline_comments=None, active_pattern_entries=None)">
623 628 %for i, line in enumerate(hunk.sideside):
624 629 <%
625 630 old_line_anchor, new_line_anchor = None, None
@@ -669,7 +674,7 b' def get_comments_for(diff_type, comments'
669 674 <span class="cb-code"><span class="cb-action ${action_class(line.original.action)}"></span>${line.original.content or '' | n}</span>
670 675
671 676 %if use_comments and line.original.lineno and line_old_comments:
672 ${inline_comments_container(line_old_comments, inline_comments)}
677 ${inline_comments_container(line_old_comments, active_pattern_entries=active_pattern_entries)}
673 678 %endif
674 679
675 680 </td>
@@ -711,7 +716,7 b' def get_comments_for(diff_type, comments'
711 716 %endif
712 717 <span class="cb-code"><span class="cb-action ${action_class(line.modified.action)}"></span>${line.modified.content or '' | n}</span>
713 718 %if use_comments and line.modified.lineno and line_new_comments:
714 ${inline_comments_container(line_new_comments, inline_comments)}
719 ${inline_comments_container(line_new_comments, active_pattern_entries=active_pattern_entries)}
715 720 %endif
716 721 </td>
717 722 </tr>
@@ -719,7 +724,7 b' def get_comments_for(diff_type, comments'
719 724 </%def>
720 725
721 726
722 <%def name="render_hunk_lines_unified(filediff, hunk, use_comments=False, inline_comments=None)">
727 <%def name="render_hunk_lines_unified(filediff, hunk, use_comments=False, inline_comments=None, active_pattern_entries=None)">
723 728 %for old_line_no, new_line_no, action, content, comments_args in hunk.unified:
724 729
725 730 <%
@@ -777,7 +782,7 b' def get_comments_for(diff_type, comments'
777 782 %endif
778 783 <span class="cb-code"><span class="cb-action ${action_class(action)}"></span> ${content or '' | n}</span>
779 784 %if use_comments and comments:
780 ${inline_comments_container(comments, inline_comments)}
785 ${inline_comments_container(comments, active_pattern_entries=active_pattern_entries)}
781 786 %endif
782 787 </td>
783 788 </tr>
@@ -785,11 +790,11 b' def get_comments_for(diff_type, comments'
785 790 </%def>
786 791
787 792
788 <%def name="render_hunk_lines(filediff, diff_mode, hunk, use_comments, inline_comments)">
793 <%def name="render_hunk_lines(filediff, diff_mode, hunk, use_comments, inline_comments, active_pattern_entries)">
789 794 % if diff_mode == 'unified':
790 ${render_hunk_lines_unified(filediff, hunk, use_comments=use_comments, inline_comments=inline_comments)}
795 ${render_hunk_lines_unified(filediff, hunk, use_comments=use_comments, inline_comments=inline_comments, active_pattern_entries=active_pattern_entries)}
791 796 % elif diff_mode == 'sideside':
792 ${render_hunk_lines_sideside(filediff, hunk, use_comments=use_comments, inline_comments=inline_comments)}
797 ${render_hunk_lines_sideside(filediff, hunk, use_comments=use_comments, inline_comments=inline_comments, active_pattern_entries=active_pattern_entries)}
793 798 % else:
794 799 <tr class="cb-line">
795 800 <td>unknown diff mode</td>
@@ -310,9 +310,9 b''
310 310
311 311 <%def name="gist_access_id(gist_access_id, full_contact)">
312 312 <div>
313 <b>
314 <a href="${h.route_path('gist_show', gist_id=gist_access_id)}">gist: ${gist_access_id}</a>
315 </b>
313 <code>
314 <a href="${h.route_path('gist_show', gist_id=gist_access_id)}">${gist_access_id}</a>
315 </code>
316 316 </div>
317 317 </%def>
318 318
@@ -21,7 +21,7 b''
21 21 <ul>
22 22 % for elem in sorted(c.email_types.keys()):
23 23 <li>
24 <a href="${request.route_path('debug_style_email', email_id=elem, _query={'user':c.rhodecode_user.username})}">${elem}</a>
24 <a href="${request.route_path('debug_style_email', email_id=elem, _query={'user':c.rhodecode_user.username, 'email': ''})}">${elem}</a>
25 25 |
26 26 <a href="${request.route_path('debug_style_email_plain_rendered', email_id=elem, _query={'user':c.rhodecode_user.username})}">plain rendered</a>
27 27 </li>
@@ -63,8 +63,8 b" css_style = ';'.join(["
63 63
64 64 ## Constants
65 65 <%
66 text_regular = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;"
67 text_monospace = "'Menlo', 'Liberation Mono', 'Consolas', 'DejaVu Sans Mono', 'Ubuntu Mono', 'Courier New', 'andale mono', 'lucida console', monospace;"
66 text_regular = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', Helvetica, sans-serif"
67 text_monospace = "'Menlo', 'Liberation Mono', 'Consolas', 'DejaVu Sans Mono', 'Ubuntu Mono', 'Courier New', 'andale mono', 'lucida console', monospace"
68 68
69 69 %>
70 70
@@ -100,7 +100,7 b' text_monospace = "\'Menlo\', \'Liberation M'
100 100 -ms-text-size-adjust: 100%;
101 101 margin: 0;
102 102 padding: 0;
103 font-family: ${text_regular|n}
103 font-family: ${text_regular|n};
104 104 }
105 105
106 106 /* Prevent Webkit and Windows Mobile platforms from changing default font sizes.*/
@@ -184,6 +184,13 b' text_monospace = "\'Menlo\', \'Liberation M'
184 184 mso-table-rspace: 0pt;
185 185 }
186 186
187 table tr {
188 display: table-row;
189 vertical-align: inherit;
190 border-color: inherit;
191 border-spacing: 0 3px;
192 }
193
187 194 table td {
188 195 padding: .65em 1em .65em 0;
189 196 border-collapse: collapse;
@@ -202,6 +209,10 b' text_monospace = "\'Menlo\', \'Liberation M'
202 209 outline: 1px solid #979797
203 210 }
204 211
212 code {
213 font-family: ${text_monospace|n};
214 }
215
205 216 @media only screen and (-webkit-min-device-pixel-ratio: 2) {
206 217 /* Put your iPhone 4g styles in here */
207 218 }
@@ -283,7 +294,7 b' text_monospace = "\'Menlo\', \'Liberation M'
283 294 margin: 3px 0 13px 0 !important;
284 295 color: #424242 !important;
285 296 font-size: 13px !important;
286 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
297 font-family: ${text_regular|n};
287 298 font-weight: normal !important;
288 299 overflow: visible !important;
289 300 line-height: 140% !important
@@ -361,7 +372,7 b' text_monospace = "\'Menlo\', \'Liberation M'
361 372 }
362 373
363 374 div.markdown-block code, div.markdown-block pre, div.markdown-block #ws, div.markdown-block #message {
364 font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'DejaVu Sans Mono', 'Ubuntu Mono', 'Courier New', 'andale mono', 'lucida console', monospace;
375 font-family: ${text_monospace|n};
365 376 font-size: 11px;
366 377 -webkit-border-radius: 2px;
367 378 -moz-border-radius: 2px;
@@ -490,8 +501,10 b' text_monospace = "\'Menlo\', \'Liberation M'
490 501 <![endif]-->
491 502 </head>
492 503 <body>
504
505 <div>
493 506 <!-- Wrapper/Container Table: Use a wrapper table to control the width and the background color consistently of your email. Use this approach instead of setting attributes on the body tag. -->
494 <table cellpadding="0" cellspacing="0" border="0" id="backgroundTable" align="left" style="margin:1%;width:97%;padding:0;font-family:sans-serif;font-weight:100;border:1px solid #dbd9da">
507 <table cellpadding="0" cellspacing="0" border="0" id="backgroundTable" align="left" style="margin:1%;width:97%;padding:0;font-family:${text_regular|n};font-weight:100;border:1px solid #dbd9da">
495 508 <tr>
496 509 <td valign="top" style="padding:0;">
497 510 <table cellpadding="0" cellspacing="0" border="0" align="left" width="100%">
@@ -513,11 +526,13 b' text_monospace = "\'Menlo\', \'Liberation M'
513 526 </tr>
514 527 </table>
515 528 <!-- End of wrapper table -->
529 </div>
516 530
517 <div style="clear: both"></div>
518 <div style="margin-left:1%;font-weight:100;font-size:11px;color:#666666;text-decoration:none;font-family:${text_monospace}">
531 <div style="width:100%; clear: both; height: 1px">&nbsp;</div>
532
533 <div style="margin-left:1%;font-weight:100;font-size:11px;color:#666666;text-decoration:none;font-family:${text_monospace};">
519 534 ${_('This is a notification from RhodeCode.')}
520 <a style="font-weight:100;font-size:11px;color:#666666;text-decoration:none;font-family:${text_monospace}" href="${instance_url}">
535 <a style="font-weight:100;font-size:11px;color:#666666;text-decoration:none;font-family:${text_monospace};" href="${instance_url}">
521 536 ${instance_url}
522 537 </a>
523 538 </div>
@@ -58,7 +58,14 b''
58 58 <div class="textarea editor">
59 59 ${h.textarea('description',cols=23,rows=5,class_="medium")}
60 60 <% metatags_url = h.literal('''<a href="#metatagsShow" onclick="$('#meta-tags-desc').toggle();return false">meta-tags</a>''') %>
61 <span class="help-block">${_('Plain text format with support of {metatags}. Add a README file for longer descriptions').format(metatags=metatags_url)|n}</span>
61 <span class="help-block">
62 % if c.visual.stylify_metatags:
63 ${_('Plain text format with {metatags} support.').format(metatags=metatags_url)|n}
64 % else:
65 ${_('Plain text format.')}
66 % endif
67 ${_('Add a README file for longer descriptions')}
68 </span>
62 69 <span id="meta-tags-desc" style="display: none">
63 70 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
64 71 ${dt.metatags_help()}
@@ -14,32 +14,50 b''
14 14 <div class="title">
15 15
16 16 </div>
17
17 18 <!-- end box / title -->
18 19 <div id="no_grid_data" class="table" style="display: none">
19 <h2 class="no-object-border">
20 <h2>
20 21 ${_('No repositories or repositories groups exists here.')}
21 22 </h2>
22 23 </div>
23 24
25 <div id="grid_data_loading" class="table" style="display: none">
26 <i class="icon-spin animate-spin"></i>
27 ${_('loading...')}
28 </div>
29
24 30 <div class="table">
25 <div id="groups_list_wrap" style="min-height: 200px;">
31 <div id="groups_list_wrap" style="min-height: 200px;display: none">
26 32 <table id="group_list_table" class="display" style="width: 100%;"></table>
27 33 </div>
28 34 </div>
29 35
30 36 <div class="table">
31 <div id="repos_list_wrap" style="min-height: 200px;">
37 <div id="repos_list_wrap" style="min-height: 200px;display: none">
32 38 <table id="repo_list_table" class="display" style="width: 100%;"></table>
33 39 </div>
34 40 </div>
35 41
36 42 </div>
43
37 44 <script>
38 45 $(document).ready(function () {
46 var noRepoData = null;
47 var noGroupData = null;
48 var $gridDataLoading = $('#grid_data_loading');
39 49
40 // repo group list
50 // global show loading of hidden grids
51 $(document).on('preInit.dt', function (e, settings) {
52 $gridDataLoading.show();
53 });
54
55 ## repo group list
41 56 var $groupListTable = $('#group_list_table');
42 57
58 $groupListTable.on('xhr.dt', function (e, settings, json, xhr) {
59 $gridDataLoading.hide();
60 });
43 61 $groupListTable.DataTable({
44 62 processing: true,
45 63 serverSide: true,
@@ -97,11 +115,12 b''
97 115 emptyTable: _gettext("No repository groups present.")
98 116 },
99 117 "drawCallback": function (settings, json) {
118
100 119 // hide grid if it's empty
101 120 if (settings.fnRecordsDisplay() === 0) {
102 $('#groups_list_wrap').hide();
121 noGroupData = true;
103 122 // both hidden, show no-data
104 if ($('#repos_list_wrap').is(':hidden')) {
123 if (noRepoData === true) {
105 124 $('#no_grid_data').show();
106 125 }
107 126 } else {
@@ -119,18 +138,13 b''
119 138 },
120 139 });
121 140
122 $groupListTable.on('xhr.dt', function (e, settings, json, xhr) {
123 $groupListTable.css('opacity', 1);
124 });
125 141
126 $groupListTable.on('preXhr.dt', function (e, settings, data) {
127 $groupListTable.css('opacity', 0.3);
128 });
129
130
131 ## // repo list
142 ## repo list
132 143 var $repoListTable = $('#repo_list_table');
133 144
145 $repoListTable.on('xhr.dt', function (e, settings, json, xhr) {
146 $gridDataLoading.hide();
147 });
134 148 $repoListTable.DataTable({
135 149 processing: true,
136 150 serverSide: true,
@@ -188,11 +202,13 b''
188 202 emptyTable: _gettext("No repositories present.")
189 203 },
190 204 "drawCallback": function (settings, json) {
205
191 206 // hide grid if it's empty
192 207 if (settings.fnRecordsDisplay() == 0) {
193 $('#repos_list_wrap').hide()
208 noRepoData = true;
209
194 210 // both hidden, show no-data
195 if ($('#groups_list_wrap').is(':hidden')) {
211 if (noGroupData === true) {
196 212 $('#no_grid_data').show()
197 213 }
198 214 } else {
@@ -210,14 +226,6 b''
210 226 },
211 227 });
212 228
213 $repoListTable.on('xhr.dt', function (e, settings, json, xhr) {
214 $repoListTable.css('opacity', 1);
215 });
216
217 $repoListTable.on('preXhr.dt', function (e, settings, data) {
218 $repoListTable.css('opacity', 0.3);
219 });
220
221 229 });
222 230 </script>
223 231 </%def>
@@ -514,6 +514,7 b''
514 514 // Flush changes into textarea
515 515 codeMirrorInstance.save();
516 516 prButtonLock(true, null, 'all');
517 $pullRequestSubmit.val(_gettext('Please wait creating pull request...'));
517 518 });
518 519
519 520 prButtonLock(true, "${_('Please select source and target')}", 'all');
@@ -438,6 +438,11 b''
438 438
439 439 <h2 style="text-align: center">
440 440 ${_('Cannot show diff when pull request state is changing. Current progress state')}: <span class="tag tag-merge-state-${c.pull_request.state}">${c.pull_request.state}</span>
441
442 % if c.is_super_admin:
443 <br/>
444 If you think this is an error try <a href="${h.current_route_path(request, force_state='created')}">forced state reset</a> to <span class="tag tag-merge-state-created">created</span> state.
445 % endif
441 446 </h2>
442 447
443 448 % else:
@@ -29,6 +29,7 b' import shutil'
29 29
30 30 import configobj
31 31
32 from rhodecode.model.settings import SettingsModel
32 33 from rhodecode.tests import *
33 34 from rhodecode.model.db import Repository, User, RepoGroup, UserGroup, Gist, UserEmailMap
34 35 from rhodecode.model.meta import Session
@@ -122,7 +123,7 b' class Fixture(object):'
122 123
123 124 return context()
124 125
125 def auth_restriction(self, auth_restriction):
126 def auth_restriction(self, registry, auth_restriction):
126 127 """
127 128 Context process for changing the builtin rhodecode plugin auth restrictions.
128 129 Use like:
@@ -135,26 +136,26 b' class Fixture(object):'
135 136
136 137 class context(object):
137 138 def _get_pluing(self):
138 plugin_id = 'egg:rhodecode-enterprise-ce#{}'.format(
139 RhodeCodeAuthPlugin.uid)
139 plugin_id = 'egg:rhodecode-enterprise-ce#{}'.format(RhodeCodeAuthPlugin.uid)
140 140 plugin = RhodeCodeAuthPlugin(plugin_id)
141 141 return plugin
142 142
143 143 def __enter__(self):
144 144 plugin = self._get_pluing()
145 plugin.create_or_update_setting(
146 'auth_restriction', auth_restriction)
145 plugin.create_or_update_setting('auth_restriction', auth_restriction)
147 146 Session().commit()
147 SettingsModel().invalidate_settings_cache()
148 148
149 149 def __exit__(self, exc_type, exc_val, exc_tb):
150 150 plugin = self._get_pluing()
151 151 plugin.create_or_update_setting(
152 152 'auth_restriction', RhodeCodeAuthPlugin.AUTH_RESTRICTION_NONE)
153 153 Session().commit()
154 SettingsModel().invalidate_settings_cache()
154 155
155 156 return context()
156 157
157 def scope_restriction(self, scope_restriction):
158 def scope_restriction(self, registry, scope_restriction):
158 159 """
159 160 Context process for changing the builtin rhodecode plugin scope restrictions.
160 161 Use like:
@@ -167,22 +168,22 b' class Fixture(object):'
167 168
168 169 class context(object):
169 170 def _get_pluing(self):
170 plugin_id = 'egg:rhodecode-enterprise-ce#{}'.format(
171 RhodeCodeAuthPlugin.uid)
171 plugin_id = 'egg:rhodecode-enterprise-ce#{}'.format(RhodeCodeAuthPlugin.uid)
172 172 plugin = RhodeCodeAuthPlugin(plugin_id)
173 173 return plugin
174 174
175 175 def __enter__(self):
176 176 plugin = self._get_pluing()
177 plugin.create_or_update_setting(
178 'scope_restriction', scope_restriction)
177 plugin.create_or_update_setting('scope_restriction', scope_restriction)
179 178 Session().commit()
179 SettingsModel().invalidate_settings_cache()
180 180
181 181 def __exit__(self, exc_type, exc_val, exc_tb):
182 182 plugin = self._get_pluing()
183 183 plugin.create_or_update_setting(
184 184 'scope_restriction', RhodeCodeAuthPlugin.AUTH_RESTRICTION_SCOPE_ALL)
185 185 Session().commit()
186 SettingsModel().invalidate_settings_cache()
186 187
187 188 return context()
188 189
@@ -43,6 +43,9 b' class TestIssueTrackerSettingsModel(obje'
43 43 settings_mock.return_value = input_settings
44 44 result = model.get_global_settings(cache=True)
45 45 get_settings.assert_called_once_with(cache=True)
46 for k, v in result.items():
47 v.pop('pat_compiled', None)
48
46 49 assert expected_result == result
47 50
48 51 def test_get_repo_settings_raise_exception_when_repo_is_not_set(self):
@@ -68,6 +71,8 b' class TestIssueTrackerSettingsModel(obje'
68 71 settings_mock.return_value = input_settings
69 72 result = model.get_repo_settings(cache=True)
70 73 get_settings.assert_called_once_with(cache=True)
74 for k, v in result.items():
75 v.pop('pat_compiled', None)
71 76 assert expected_result == result
72 77
73 78 @pytest.mark.parametrize("inherit_settings, method", [
@@ -209,6 +209,8 b' def enable_auth_plugins(request, baseapp'
209 209 Session().add(setting)
210 210 Session().commit()
211 211
212 SettingsModel().invalidate_settings_cache()
213
212 214 def cleanup():
213 215 _enable_plugins(['egg:rhodecode-enterprise-ce#rhodecode'])
214 216
General Comments 0
You need to be logged in to leave comments. Login now