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