Show More
@@ -1,40 +1,62 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 |
|
2 | |||
3 | # Copyright (C) 2016-2018 RhodeCode GmbH |
|
3 | # Copyright (C) 2016-2018 RhodeCode GmbH | |
4 | # |
|
4 | # | |
5 | # This program is free software: you can redistribute it and/or modify |
|
5 | # This program is free software: you can redistribute it and/or modify | |
6 | # it under the terms of the GNU Affero General Public License, version 3 |
|
6 | # it under the terms of the GNU Affero General Public License, version 3 | |
7 | # (only), as published by the Free Software Foundation. |
|
7 | # (only), as published by the Free Software Foundation. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU Affero General Public License |
|
14 | # You should have received a copy of the GNU Affero General Public License | |
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | # |
|
16 | # | |
17 | # This program is dual-licensed. If you wish to learn more about the |
|
17 | # This program is dual-licensed. If you wish to learn more about the | |
18 | # RhodeCode Enterprise Edition, including its added features, Support services, |
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |
20 |
|
20 | |||
21 |
|
21 | |||
22 | def assert_and_get_content(result): |
|
22 | def assert_and_get_main_filter_content(result): | |
23 | repos = [] |
|
23 | repos = [] | |
24 | groups = [] |
|
24 | groups = [] | |
25 | commits = [] |
|
25 | commits = [] | |
|
26 | users = [] | |||
|
27 | for data_item in result: | |||
|
28 | assert data_item['id'] | |||
|
29 | assert data_item['value'] | |||
|
30 | assert data_item['value_display'] | |||
|
31 | assert data_item['url'] | |||
|
32 | ||||
|
33 | if data_item['type'] == 'search': | |||
|
34 | assert data_item['value_display'].startswith('Full text search for:') | |||
|
35 | elif data_item['type'] == 'repo': | |||
|
36 | repos.append(data_item) | |||
|
37 | elif data_item['type'] == 'repo_group': | |||
|
38 | groups.append(data_item) | |||
|
39 | elif data_item['type'] == 'user': | |||
|
40 | users.append(data_item) | |||
|
41 | elif data_item['type'] == 'commit': | |||
|
42 | commits.append(data_item) | |||
|
43 | else: | |||
|
44 | raise Exception('invalid type `%s`' % data_item['type']) | |||
|
45 | ||||
|
46 | return repos, groups, users, commits | |||
|
47 | ||||
|
48 | ||||
|
49 | def assert_and_get_repo_list_content(result): | |||
|
50 | repos = [] | |||
26 | for data in result: |
|
51 | for data in result: | |
27 | for data_item in data['children']: |
|
52 | for data_item in data['children']: | |
28 | assert data_item['id'] |
|
53 | assert data_item['id'] | |
29 | assert data_item['text'] |
|
54 | assert data_item['text'] | |
30 | assert data_item['url'] |
|
55 | assert data_item['url'] | |
|
56 | ||||
31 | if data_item['type'] == 'repo': |
|
57 | if data_item['type'] == 'repo': | |
32 | repos.append(data_item) |
|
58 | repos.append(data_item) | |
33 | elif data_item['type'] == 'group': |
|
|||
34 | groups.append(data_item) |
|
|||
35 | elif data_item['type'] == 'commit': |
|
|||
36 | commits.append(data_item) |
|
|||
37 | else: |
|
59 | else: | |
38 | raise Exception('invalid type %s' % data_item['type']) |
|
60 | raise Exception('invalid type %s' % data_item['type']) | |
39 |
|
61 | |||
40 |
return repos |
|
62 | return repos |
@@ -1,151 +1,180 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 |
|
2 | |||
3 | # Copyright (C) 2016-2018 RhodeCode GmbH |
|
3 | # Copyright (C) 2016-2018 RhodeCode GmbH | |
4 | # |
|
4 | # | |
5 | # This program is free software: you can redistribute it and/or modify |
|
5 | # This program is free software: you can redistribute it and/or modify | |
6 | # it under the terms of the GNU Affero General Public License, version 3 |
|
6 | # it under the terms of the GNU Affero General Public License, version 3 | |
7 | # (only), as published by the Free Software Foundation. |
|
7 | # (only), as published by the Free Software Foundation. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU Affero General Public License |
|
14 | # You should have received a copy of the GNU Affero General Public License | |
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | # |
|
16 | # | |
17 | # This program is dual-licensed. If you wish to learn more about the |
|
17 | # This program is dual-licensed. If you wish to learn more about the | |
18 | # RhodeCode Enterprise Edition, including its added features, Support services, |
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |
20 |
|
20 | |||
21 | import json |
|
21 | import json | |
22 |
|
22 | |||
23 | import pytest |
|
23 | import pytest | |
24 |
|
24 | |||
25 | from . import assert_and_get_content |
|
25 | from . import assert_and_get_main_filter_content | |
26 | from rhodecode.tests import TestController, TEST_USER_ADMIN_LOGIN |
|
26 | from rhodecode.tests import TestController, TEST_USER_ADMIN_LOGIN | |
27 | from rhodecode.tests.fixture import Fixture |
|
27 | from rhodecode.tests.fixture import Fixture | |
28 |
|
28 | |||
29 | from rhodecode.lib.utils import map_groups |
|
29 | from rhodecode.lib.utils import map_groups | |
30 | from rhodecode.model.repo import RepoModel |
|
30 | from rhodecode.model.repo import RepoModel | |
31 | from rhodecode.model.repo_group import RepoGroupModel |
|
31 | from rhodecode.model.repo_group import RepoGroupModel | |
32 | from rhodecode.model.db import Session, Repository, RepoGroup |
|
32 | from rhodecode.model.db import Session, Repository, RepoGroup | |
33 |
|
33 | |||
34 | fixture = Fixture() |
|
34 | fixture = Fixture() | |
35 |
|
35 | |||
36 |
|
36 | |||
37 | def route_path(name, params=None, **kwargs): |
|
37 | def route_path(name, params=None, **kwargs): | |
38 | import urllib |
|
38 | import urllib | |
39 |
|
39 | |||
40 | base_url = { |
|
40 | base_url = { | |
41 | 'goto_switcher_data': '/_goto_data', |
|
41 | 'goto_switcher_data': '/_goto_data', | |
42 | }[name].format(**kwargs) |
|
42 | }[name].format(**kwargs) | |
43 |
|
43 | |||
44 | if params: |
|
44 | if params: | |
45 | base_url = '{}?{}'.format(base_url, urllib.urlencode(params)) |
|
45 | base_url = '{}?{}'.format(base_url, urllib.urlencode(params)) | |
46 | return base_url |
|
46 | return base_url | |
47 |
|
47 | |||
48 |
|
48 | |||
49 | class TestGotoSwitcherData(TestController): |
|
49 | class TestGotoSwitcherData(TestController): | |
50 |
|
50 | |||
51 | required_repos_with_groups = [ |
|
51 | required_repos_with_groups = [ | |
52 | 'abc', |
|
52 | 'abc', | |
53 | 'abc-fork', |
|
53 | 'abc-fork', | |
54 | 'forks/abcd', |
|
54 | 'forks/abcd', | |
55 | 'abcd', |
|
55 | 'abcd', | |
56 | 'abcde', |
|
56 | 'abcde', | |
57 | 'a/abc', |
|
57 | 'a/abc', | |
58 | 'aa/abc', |
|
58 | 'aa/abc', | |
59 | 'aaa/abc', |
|
59 | 'aaa/abc', | |
60 | 'aaaa/abc', |
|
60 | 'aaaa/abc', | |
61 | 'repos_abc/aaa/abc', |
|
61 | 'repos_abc/aaa/abc', | |
62 | 'abc_repos/abc', |
|
62 | 'abc_repos/abc', | |
63 | 'abc_repos/abcd', |
|
63 | 'abc_repos/abcd', | |
64 | 'xxx/xyz', |
|
64 | 'xxx/xyz', | |
65 | 'forked-abc/a/abc' |
|
65 | 'forked-abc/a/abc' | |
66 | ] |
|
66 | ] | |
67 |
|
67 | |||
68 | @pytest.fixture(autouse=True, scope='class') |
|
68 | @pytest.fixture(autouse=True, scope='class') | |
69 | def prepare(self, request, baseapp): |
|
69 | def prepare(self, request, baseapp): | |
70 | for repo_and_group in self.required_repos_with_groups: |
|
70 | for repo_and_group in self.required_repos_with_groups: | |
71 | # create structure of groups and return the last group |
|
71 | # create structure of groups and return the last group | |
72 |
|
72 | |||
73 | repo_group = map_groups(repo_and_group) |
|
73 | repo_group = map_groups(repo_and_group) | |
74 |
|
74 | |||
75 | RepoModel()._create_repo( |
|
75 | RepoModel()._create_repo( | |
76 | repo_and_group, 'hg', 'test-ac', TEST_USER_ADMIN_LOGIN, |
|
76 | repo_and_group, 'hg', 'test-ac', TEST_USER_ADMIN_LOGIN, | |
77 | repo_group=getattr(repo_group, 'group_id', None)) |
|
77 | repo_group=getattr(repo_group, 'group_id', None)) | |
78 |
|
78 | |||
79 | Session().commit() |
|
79 | Session().commit() | |
80 |
|
80 | |||
81 | request.addfinalizer(self.cleanup) |
|
81 | request.addfinalizer(self.cleanup) | |
82 |
|
82 | |||
83 | def cleanup(self): |
|
83 | def cleanup(self): | |
84 | # first delete all repos |
|
84 | # first delete all repos | |
85 | for repo_and_groups in self.required_repos_with_groups: |
|
85 | for repo_and_groups in self.required_repos_with_groups: | |
86 | repo = Repository.get_by_repo_name(repo_and_groups) |
|
86 | repo = Repository.get_by_repo_name(repo_and_groups) | |
87 | if repo: |
|
87 | if repo: | |
88 | RepoModel().delete(repo) |
|
88 | RepoModel().delete(repo) | |
89 | Session().commit() |
|
89 | Session().commit() | |
90 |
|
90 | |||
91 | # then delete all empty groups |
|
91 | # then delete all empty groups | |
92 | for repo_and_groups in self.required_repos_with_groups: |
|
92 | for repo_and_groups in self.required_repos_with_groups: | |
93 | if '/' in repo_and_groups: |
|
93 | if '/' in repo_and_groups: | |
94 | r_group = repo_and_groups.rsplit('/', 1)[0] |
|
94 | r_group = repo_and_groups.rsplit('/', 1)[0] | |
95 | repo_group = RepoGroup.get_by_group_name(r_group) |
|
95 | repo_group = RepoGroup.get_by_group_name(r_group) | |
96 | if not repo_group: |
|
96 | if not repo_group: | |
97 | continue |
|
97 | continue | |
98 | parents = repo_group.parents |
|
98 | parents = repo_group.parents | |
99 | RepoGroupModel().delete(repo_group, force_delete=True) |
|
99 | RepoGroupModel().delete(repo_group, force_delete=True) | |
100 | Session().commit() |
|
100 | Session().commit() | |
101 |
|
101 | |||
102 | for el in reversed(parents): |
|
102 | for el in reversed(parents): | |
103 | RepoGroupModel().delete(el, force_delete=True) |
|
103 | RepoGroupModel().delete(el, force_delete=True) | |
104 | Session().commit() |
|
104 | Session().commit() | |
105 |
|
105 | |||
106 |
def test_ |
|
106 | def test_empty_query(self, xhr_header): | |
107 | self.log_user() |
|
107 | self.log_user() | |
108 |
|
108 | |||
109 | response = self.app.get( |
|
109 | response = self.app.get( | |
110 | route_path('goto_switcher_data'), |
|
110 | route_path('goto_switcher_data'), | |
111 | extra_environ=xhr_header, status=200) |
|
111 | extra_environ=xhr_header, status=200) | |
112 |
result = json.loads(response.body)[' |
|
112 | result = json.loads(response.body)['suggestions'] | |
113 |
|
||||
114 | repos, groups, commits = assert_and_get_content(result) |
|
|||
115 |
|
113 | |||
116 | assert len(repos) == len(Repository.get_all()) |
|
114 | assert result == [] | |
117 | assert len(groups) == len(RepoGroup.get_all()) |
|
|||
118 | assert len(commits) == 0 |
|
|||
119 |
|
115 | |||
120 | def test_returns_list_of_repos_and_groups_filtered(self, xhr_header): |
|
116 | def test_returns_list_of_repos_and_groups_filtered(self, xhr_header): | |
121 | self.log_user() |
|
117 | self.log_user() | |
122 |
|
118 | |||
123 | response = self.app.get( |
|
119 | response = self.app.get( | |
124 | route_path('goto_switcher_data'), |
|
120 | route_path('goto_switcher_data'), | |
125 | params={'query': 'abc'}, |
|
121 | params={'query': 'abc'}, | |
126 | extra_environ=xhr_header, status=200) |
|
122 | extra_environ=xhr_header, status=200) | |
127 |
result = json.loads(response.body)[' |
|
123 | result = json.loads(response.body)['suggestions'] | |
128 |
|
124 | |||
129 | repos, groups, commits = assert_and_get_content(result) |
|
125 | repos, groups, users, commits = assert_and_get_main_filter_content(result) | |
130 |
|
126 | |||
131 | assert len(repos) == 13 |
|
127 | assert len(repos) == 13 | |
132 | assert len(groups) == 5 |
|
128 | assert len(groups) == 5 | |
|
129 | assert len(users) == 0 | |||
133 | assert len(commits) == 0 |
|
130 | assert len(commits) == 0 | |
134 |
|
131 | |||
|
132 | def test_returns_list_of_users_filtered(self, xhr_header): | |||
|
133 | self.log_user() | |||
|
134 | ||||
|
135 | response = self.app.get( | |||
|
136 | route_path('goto_switcher_data'), | |||
|
137 | params={'query': 'user:admin'}, | |||
|
138 | extra_environ=xhr_header, status=200) | |||
|
139 | result = json.loads(response.body)['suggestions'] | |||
|
140 | ||||
|
141 | repos, groups, users, commits = assert_and_get_main_filter_content(result) | |||
|
142 | ||||
|
143 | assert len(repos) == 0 | |||
|
144 | assert len(groups) == 0 | |||
|
145 | assert len(users) == 1 | |||
|
146 | assert len(commits) == 0 | |||
|
147 | ||||
|
148 | def test_returns_list_of_commits_filtered(self, xhr_header): | |||
|
149 | self.log_user() | |||
|
150 | ||||
|
151 | response = self.app.get( | |||
|
152 | route_path('goto_switcher_data'), | |||
|
153 | params={'query': 'commit:e8'}, | |||
|
154 | extra_environ=xhr_header, status=200) | |||
|
155 | result = json.loads(response.body)['suggestions'] | |||
|
156 | ||||
|
157 | repos, groups, users, commits = assert_and_get_main_filter_content(result) | |||
|
158 | ||||
|
159 | assert len(repos) == 0 | |||
|
160 | assert len(groups) == 0 | |||
|
161 | assert len(users) == 0 | |||
|
162 | assert len(commits) == 5 | |||
|
163 | ||||
135 | def test_returns_list_of_properly_sorted_and_filtered(self, xhr_header): |
|
164 | def test_returns_list_of_properly_sorted_and_filtered(self, xhr_header): | |
136 | self.log_user() |
|
165 | self.log_user() | |
137 |
|
166 | |||
138 | response = self.app.get( |
|
167 | response = self.app.get( | |
139 | route_path('goto_switcher_data'), |
|
168 | route_path('goto_switcher_data'), | |
140 | params={'query': 'abc'}, |
|
169 | params={'query': 'abc'}, | |
141 | extra_environ=xhr_header, status=200) |
|
170 | extra_environ=xhr_header, status=200) | |
142 |
result = json.loads(response.body)[' |
|
171 | result = json.loads(response.body)['suggestions'] | |
143 |
|
172 | |||
144 | repos, groups, commits = assert_and_get_content(result) |
|
173 | repos, groups, users, commits = assert_and_get_main_filter_content(result) | |
145 |
|
174 | |||
146 |
test_repos = [x[' |
|
175 | test_repos = [x['value_display'] for x in repos[:4]] | |
147 | assert ['abc', 'abcd', 'a/abc', 'abcde'] == test_repos |
|
176 | assert ['abc', 'abcd', 'a/abc', 'abcde'] == test_repos | |
148 |
|
177 | |||
149 |
test_groups = [x[' |
|
178 | test_groups = [x['value_display'] for x in groups[:4]] | |
150 | assert ['abc_repos', 'repos_abc', |
|
179 | assert ['abc_repos', 'repos_abc', | |
151 | 'forked-abc', 'forked-abc/a'] == test_groups |
|
180 | 'forked-abc', 'forked-abc/a'] == test_groups |
@@ -1,103 +1,95 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 |
|
2 | |||
3 | # Copyright (C) 2016-2018 RhodeCode GmbH |
|
3 | # Copyright (C) 2016-2018 RhodeCode GmbH | |
4 | # |
|
4 | # | |
5 | # This program is free software: you can redistribute it and/or modify |
|
5 | # This program is free software: you can redistribute it and/or modify | |
6 | # it under the terms of the GNU Affero General Public License, version 3 |
|
6 | # it under the terms of the GNU Affero General Public License, version 3 | |
7 | # (only), as published by the Free Software Foundation. |
|
7 | # (only), as published by the Free Software Foundation. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU Affero General Public License |
|
14 | # You should have received a copy of the GNU Affero General Public License | |
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | # |
|
16 | # | |
17 | # This program is dual-licensed. If you wish to learn more about the |
|
17 | # This program is dual-licensed. If you wish to learn more about the | |
18 | # RhodeCode Enterprise Edition, including its added features, Support services, |
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |
20 |
|
20 | |||
21 | import json |
|
21 | import json | |
22 |
|
22 | |||
23 | from . import assert_and_get_content |
|
23 | from . import assert_and_get_repo_list_content | |
24 | from rhodecode.tests import TestController |
|
24 | from rhodecode.tests import TestController | |
25 | from rhodecode.tests.fixture import Fixture |
|
25 | from rhodecode.tests.fixture import Fixture | |
26 | from rhodecode.model.db import Repository |
|
26 | from rhodecode.model.db import Repository | |
27 |
|
27 | |||
28 | fixture = Fixture() |
|
28 | fixture = Fixture() | |
29 |
|
29 | |||
30 |
|
30 | |||
31 | def route_path(name, params=None, **kwargs): |
|
31 | def route_path(name, params=None, **kwargs): | |
32 | import urllib |
|
32 | import urllib | |
33 |
|
33 | |||
34 | base_url = { |
|
34 | base_url = { | |
35 | 'repo_list_data': '/_repos', |
|
35 | 'repo_list_data': '/_repos', | |
36 | }[name].format(**kwargs) |
|
36 | }[name].format(**kwargs) | |
37 |
|
37 | |||
38 | if params: |
|
38 | if params: | |
39 | base_url = '{}?{}'.format(base_url, urllib.urlencode(params)) |
|
39 | base_url = '{}?{}'.format(base_url, urllib.urlencode(params)) | |
40 | return base_url |
|
40 | return base_url | |
41 |
|
41 | |||
42 |
|
42 | |||
43 | class TestRepoListData(TestController): |
|
43 | class TestRepoListData(TestController): | |
44 |
|
44 | |||
45 | def test_returns_list_of_repos_and_groups(self, xhr_header): |
|
45 | def test_returns_list_of_repos_and_groups(self, xhr_header): | |
46 | self.log_user() |
|
46 | self.log_user() | |
47 |
|
47 | |||
48 | response = self.app.get( |
|
48 | response = self.app.get( | |
49 | route_path('repo_list_data'), |
|
49 | route_path('repo_list_data'), | |
50 | extra_environ=xhr_header, status=200) |
|
50 | extra_environ=xhr_header, status=200) | |
51 | result = json.loads(response.body)['results'] |
|
51 | result = json.loads(response.body)['results'] | |
52 |
|
52 | |||
53 |
repos |
|
53 | repos = assert_and_get_repo_list_content(result) | |
54 |
|
54 | |||
55 | assert len(repos) == len(Repository.get_all()) |
|
55 | assert len(repos) == len(Repository.get_all()) | |
56 | assert len(groups) == 0 |
|
|||
57 | assert len(commits) == 0 |
|
|||
58 |
|
56 | |||
59 | def test_returns_list_of_repos_and_groups_filtered(self, xhr_header): |
|
57 | def test_returns_list_of_repos_and_groups_filtered(self, xhr_header): | |
60 | self.log_user() |
|
58 | self.log_user() | |
61 |
|
59 | |||
62 | response = self.app.get( |
|
60 | response = self.app.get( | |
63 | route_path('repo_list_data'), |
|
61 | route_path('repo_list_data'), | |
64 | params={'query': 'vcs_test_git'}, |
|
62 | params={'query': 'vcs_test_git'}, | |
65 | extra_environ=xhr_header, status=200) |
|
63 | extra_environ=xhr_header, status=200) | |
66 | result = json.loads(response.body)['results'] |
|
64 | result = json.loads(response.body)['results'] | |
67 |
|
65 | |||
68 |
repos |
|
66 | repos = assert_and_get_repo_list_content(result) | |
69 |
|
67 | |||
70 | assert len(repos) == len(Repository.query().filter( |
|
68 | assert len(repos) == len(Repository.query().filter( | |
71 | Repository.repo_name.ilike('%vcs_test_git%')).all()) |
|
69 | Repository.repo_name.ilike('%vcs_test_git%')).all()) | |
72 | assert len(groups) == 0 |
|
|||
73 | assert len(commits) == 0 |
|
|||
74 |
|
70 | |||
75 | def test_returns_list_of_repos_and_groups_filtered_with_type(self, xhr_header): |
|
71 | def test_returns_list_of_repos_and_groups_filtered_with_type(self, xhr_header): | |
76 | self.log_user() |
|
72 | self.log_user() | |
77 |
|
73 | |||
78 | response = self.app.get( |
|
74 | response = self.app.get( | |
79 | route_path('repo_list_data'), |
|
75 | route_path('repo_list_data'), | |
80 | params={'query': 'vcs_test_git', 'repo_type': 'git'}, |
|
76 | params={'query': 'vcs_test_git', 'repo_type': 'git'}, | |
81 | extra_environ=xhr_header, status=200) |
|
77 | extra_environ=xhr_header, status=200) | |
82 | result = json.loads(response.body)['results'] |
|
78 | result = json.loads(response.body)['results'] | |
83 |
|
79 | |||
84 |
repos |
|
80 | repos = assert_and_get_repo_list_content(result) | |
85 |
|
81 | |||
86 | assert len(repos) == len(Repository.query().filter( |
|
82 | assert len(repos) == len(Repository.query().filter( | |
87 | Repository.repo_name.ilike('%vcs_test_git%')).all()) |
|
83 | Repository.repo_name.ilike('%vcs_test_git%')).all()) | |
88 | assert len(groups) == 0 |
|
|||
89 | assert len(commits) == 0 |
|
|||
90 |
|
84 | |||
91 | def test_returns_list_of_repos_non_ascii_query(self, xhr_header): |
|
85 | def test_returns_list_of_repos_non_ascii_query(self, xhr_header): | |
92 | self.log_user() |
|
86 | self.log_user() | |
93 | response = self.app.get( |
|
87 | response = self.app.get( | |
94 | route_path('repo_list_data'), |
|
88 | route_path('repo_list_data'), | |
95 | params={'query': 'Δ_vcs_test_Δ ', 'repo_type': 'git'}, |
|
89 | params={'query': 'Δ_vcs_test_Δ ', 'repo_type': 'git'}, | |
96 | extra_environ=xhr_header, status=200) |
|
90 | extra_environ=xhr_header, status=200) | |
97 | result = json.loads(response.body)['results'] |
|
91 | result = json.loads(response.body)['results'] | |
98 |
|
92 | |||
99 |
repos |
|
93 | repos = assert_and_get_repo_list_content(result) | |
100 |
|
94 | |||
101 | assert len(repos) == 0 |
|
95 | assert len(repos) == 0 | |
102 | assert len(groups) == 0 |
|
|||
103 | assert len(commits) == 0 |
|
@@ -1,325 +1,374 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 |
|
2 | |||
3 | # Copyright (C) 2016-2018 RhodeCode GmbH |
|
3 | # Copyright (C) 2016-2018 RhodeCode GmbH | |
4 | # |
|
4 | # | |
5 | # This program is free software: you can redistribute it and/or modify |
|
5 | # This program is free software: you can redistribute it and/or modify | |
6 | # it under the terms of the GNU Affero General Public License, version 3 |
|
6 | # it under the terms of the GNU Affero General Public License, version 3 | |
7 | # (only), as published by the Free Software Foundation. |
|
7 | # (only), as published by the Free Software Foundation. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU Affero General Public License |
|
14 | # You should have received a copy of the GNU Affero General Public License | |
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | # |
|
16 | # | |
17 | # This program is dual-licensed. If you wish to learn more about the |
|
17 | # This program is dual-licensed. If you wish to learn more about the | |
18 | # RhodeCode Enterprise Edition, including its added features, Support services, |
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |
20 |
|
20 | |||
21 | import re |
|
21 | import re | |
22 | import logging |
|
22 | import logging | |
|
23 | import collections | |||
23 |
|
24 | |||
24 | from pyramid.view import view_config |
|
25 | from pyramid.view import view_config | |
25 |
|
26 | |||
26 | from rhodecode.apps._base import BaseAppView |
|
27 | from rhodecode.apps._base import BaseAppView | |
27 | from rhodecode.lib import helpers as h |
|
28 | from rhodecode.lib import helpers as h | |
28 | from rhodecode.lib.auth import ( |
|
29 | from rhodecode.lib.auth import ( | |
29 | LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator) |
|
30 | LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator) | |
30 | from rhodecode.lib.index import searcher_from_config |
|
31 | from rhodecode.lib.index import searcher_from_config | |
31 | from rhodecode.lib.utils2 import safe_unicode, str2bool |
|
32 | from rhodecode.lib.utils2 import safe_unicode, str2bool | |
32 | from rhodecode.lib.ext_json import json |
|
33 | from rhodecode.lib.ext_json import json | |
33 | from rhodecode.model.db import ( |
|
34 | from rhodecode.model.db import ( | |
34 | func, or_, in_filter_generator, Repository, RepoGroup) |
|
35 | func, or_, in_filter_generator, Repository, RepoGroup, User, UserGroup) | |
35 | from rhodecode.model.repo import RepoModel |
|
36 | from rhodecode.model.repo import RepoModel | |
36 | from rhodecode.model.repo_group import RepoGroupModel |
|
37 | from rhodecode.model.repo_group import RepoGroupModel | |
37 | from rhodecode.model.scm import RepoGroupList, RepoList |
|
38 | from rhodecode.model.scm import RepoGroupList, RepoList | |
38 | from rhodecode.model.user import UserModel |
|
39 | from rhodecode.model.user import UserModel | |
39 | from rhodecode.model.user_group import UserGroupModel |
|
40 | from rhodecode.model.user_group import UserGroupModel | |
40 |
|
41 | |||
41 | log = logging.getLogger(__name__) |
|
42 | log = logging.getLogger(__name__) | |
42 |
|
43 | |||
43 |
|
44 | |||
44 | class HomeView(BaseAppView): |
|
45 | class HomeView(BaseAppView): | |
45 |
|
46 | |||
46 | def load_default_context(self): |
|
47 | def load_default_context(self): | |
47 | c = self._get_local_tmpl_context() |
|
48 | c = self._get_local_tmpl_context() | |
48 | c.user = c.auth_user.get_instance() |
|
49 | c.user = c.auth_user.get_instance() | |
49 |
|
50 | |||
50 | return c |
|
51 | return c | |
51 |
|
52 | |||
52 | @LoginRequired() |
|
53 | @LoginRequired() | |
53 | @view_config( |
|
54 | @view_config( | |
54 | route_name='user_autocomplete_data', request_method='GET', |
|
55 | route_name='user_autocomplete_data', request_method='GET', | |
55 | renderer='json_ext', xhr=True) |
|
56 | renderer='json_ext', xhr=True) | |
56 | def user_autocomplete_data(self): |
|
57 | def user_autocomplete_data(self): | |
57 | self.load_default_context() |
|
58 | self.load_default_context() | |
58 | query = self.request.GET.get('query') |
|
59 | query = self.request.GET.get('query') | |
59 | active = str2bool(self.request.GET.get('active') or True) |
|
60 | active = str2bool(self.request.GET.get('active') or True) | |
60 | include_groups = str2bool(self.request.GET.get('user_groups')) |
|
61 | include_groups = str2bool(self.request.GET.get('user_groups')) | |
61 | expand_groups = str2bool(self.request.GET.get('user_groups_expand')) |
|
62 | expand_groups = str2bool(self.request.GET.get('user_groups_expand')) | |
62 | skip_default_user = str2bool(self.request.GET.get('skip_default_user')) |
|
63 | skip_default_user = str2bool(self.request.GET.get('skip_default_user')) | |
63 |
|
64 | |||
64 | log.debug('generating user list, query:%s, active:%s, with_groups:%s', |
|
65 | log.debug('generating user list, query:%s, active:%s, with_groups:%s', | |
65 | query, active, include_groups) |
|
66 | query, active, include_groups) | |
66 |
|
67 | |||
67 | _users = UserModel().get_users( |
|
68 | _users = UserModel().get_users( | |
68 | name_contains=query, only_active=active) |
|
69 | name_contains=query, only_active=active) | |
69 |
|
70 | |||
70 | def maybe_skip_default_user(usr): |
|
71 | def maybe_skip_default_user(usr): | |
71 | if skip_default_user and usr['username'] == UserModel.cls.DEFAULT_USER: |
|
72 | if skip_default_user and usr['username'] == UserModel.cls.DEFAULT_USER: | |
72 | return False |
|
73 | return False | |
73 | return True |
|
74 | return True | |
74 | _users = filter(maybe_skip_default_user, _users) |
|
75 | _users = filter(maybe_skip_default_user, _users) | |
75 |
|
76 | |||
76 | if include_groups: |
|
77 | if include_groups: | |
77 | # extend with user groups |
|
78 | # extend with user groups | |
78 | _user_groups = UserGroupModel().get_user_groups( |
|
79 | _user_groups = UserGroupModel().get_user_groups( | |
79 | name_contains=query, only_active=active, |
|
80 | name_contains=query, only_active=active, | |
80 | expand_groups=expand_groups) |
|
81 | expand_groups=expand_groups) | |
81 | _users = _users + _user_groups |
|
82 | _users = _users + _user_groups | |
82 |
|
83 | |||
83 | return {'suggestions': _users} |
|
84 | return {'suggestions': _users} | |
84 |
|
85 | |||
85 | @LoginRequired() |
|
86 | @LoginRequired() | |
86 | @NotAnonymous() |
|
87 | @NotAnonymous() | |
87 | @view_config( |
|
88 | @view_config( | |
88 | route_name='user_group_autocomplete_data', request_method='GET', |
|
89 | route_name='user_group_autocomplete_data', request_method='GET', | |
89 | renderer='json_ext', xhr=True) |
|
90 | renderer='json_ext', xhr=True) | |
90 | def user_group_autocomplete_data(self): |
|
91 | def user_group_autocomplete_data(self): | |
91 | self.load_default_context() |
|
92 | self.load_default_context() | |
92 | query = self.request.GET.get('query') |
|
93 | query = self.request.GET.get('query') | |
93 | active = str2bool(self.request.GET.get('active') or True) |
|
94 | active = str2bool(self.request.GET.get('active') or True) | |
94 | expand_groups = str2bool(self.request.GET.get('user_groups_expand')) |
|
95 | expand_groups = str2bool(self.request.GET.get('user_groups_expand')) | |
95 |
|
96 | |||
96 | log.debug('generating user group list, query:%s, active:%s', |
|
97 | log.debug('generating user group list, query:%s, active:%s', | |
97 | query, active) |
|
98 | query, active) | |
98 |
|
99 | |||
99 | _user_groups = UserGroupModel().get_user_groups( |
|
100 | _user_groups = UserGroupModel().get_user_groups( | |
100 | name_contains=query, only_active=active, |
|
101 | name_contains=query, only_active=active, | |
101 | expand_groups=expand_groups) |
|
102 | expand_groups=expand_groups) | |
102 | _user_groups = _user_groups |
|
103 | _user_groups = _user_groups | |
103 |
|
104 | |||
104 | return {'suggestions': _user_groups} |
|
105 | return {'suggestions': _user_groups} | |
105 |
|
106 | |||
106 | def _get_repo_list(self, name_contains=None, repo_type=None, limit=20): |
|
107 | def _get_repo_list(self, name_contains=None, repo_type=None, limit=20): | |
|
108 | org_query = name_contains | |||
107 | allowed_ids = self._rhodecode_user.repo_acl_ids( |
|
109 | allowed_ids = self._rhodecode_user.repo_acl_ids( | |
108 | ['repository.read', 'repository.write', 'repository.admin'], |
|
110 | ['repository.read', 'repository.write', 'repository.admin'], | |
109 | cache=False, name_filter=name_contains) or [-1] |
|
111 | cache=False, name_filter=name_contains) or [-1] | |
110 |
|
112 | |||
111 | query = Repository.query()\ |
|
113 | query = Repository.query()\ | |
112 | .order_by(func.length(Repository.repo_name))\ |
|
114 | .order_by(func.length(Repository.repo_name))\ | |
113 | .order_by(Repository.repo_name)\ |
|
115 | .order_by(Repository.repo_name)\ | |
114 | .filter(or_( |
|
116 | .filter(or_( | |
115 | # generate multiple IN to fix limitation problems |
|
117 | # generate multiple IN to fix limitation problems | |
116 | *in_filter_generator(Repository.repo_id, allowed_ids) |
|
118 | *in_filter_generator(Repository.repo_id, allowed_ids) | |
117 | )) |
|
119 | )) | |
118 |
|
120 | |||
119 | if repo_type: |
|
121 | if repo_type: | |
120 | query = query.filter(Repository.repo_type == repo_type) |
|
122 | query = query.filter(Repository.repo_type == repo_type) | |
121 |
|
123 | |||
122 | if name_contains: |
|
124 | if name_contains: | |
123 | ilike_expression = u'%{}%'.format(safe_unicode(name_contains)) |
|
125 | ilike_expression = u'%{}%'.format(safe_unicode(name_contains)) | |
124 | query = query.filter( |
|
126 | query = query.filter( | |
125 | Repository.repo_name.ilike(ilike_expression)) |
|
127 | Repository.repo_name.ilike(ilike_expression)) | |
126 | query = query.limit(limit) |
|
128 | query = query.limit(limit) | |
127 |
|
129 | |||
128 |
acl_ |
|
130 | acl_iter = query | |
129 |
|
131 | |||
130 | return [ |
|
132 | return [ | |
131 | { |
|
133 | { | |
132 | 'id': obj.repo_name, |
|
134 | 'id': obj.repo_name, | |
|
135 | 'value': org_query, | |||
|
136 | 'value_display': obj.repo_name, | |||
133 | 'text': obj.repo_name, |
|
137 | 'text': obj.repo_name, | |
134 | 'type': 'repo', |
|
138 | 'type': 'repo', | |
135 |
' |
|
139 | 'repo_id': obj.repo_id, | |
136 |
|
|
140 | 'repo_type': obj.repo_type, | |
|
141 | 'private': obj.private, | |||
137 | 'url': h.route_path('repo_summary', repo_name=obj.repo_name) |
|
142 | 'url': h.route_path('repo_summary', repo_name=obj.repo_name) | |
138 | } |
|
143 | } | |
139 |
for obj in acl_ |
|
144 | for obj in acl_iter] | |
140 |
|
145 | |||
141 | def _get_repo_group_list(self, name_contains=None, limit=20): |
|
146 | def _get_repo_group_list(self, name_contains=None, limit=20): | |
|
147 | org_query = name_contains | |||
142 | allowed_ids = self._rhodecode_user.repo_group_acl_ids( |
|
148 | allowed_ids = self._rhodecode_user.repo_group_acl_ids( | |
143 | ['group.read', 'group.write', 'group.admin'], |
|
149 | ['group.read', 'group.write', 'group.admin'], | |
144 | cache=False, name_filter=name_contains) or [-1] |
|
150 | cache=False, name_filter=name_contains) or [-1] | |
145 |
|
151 | |||
146 | query = RepoGroup.query()\ |
|
152 | query = RepoGroup.query()\ | |
147 | .order_by(func.length(RepoGroup.group_name))\ |
|
153 | .order_by(func.length(RepoGroup.group_name))\ | |
148 | .order_by(RepoGroup.group_name) \ |
|
154 | .order_by(RepoGroup.group_name) \ | |
149 | .filter(or_( |
|
155 | .filter(or_( | |
150 | # generate multiple IN to fix limitation problems |
|
156 | # generate multiple IN to fix limitation problems | |
151 | *in_filter_generator(RepoGroup.group_id, allowed_ids) |
|
157 | *in_filter_generator(RepoGroup.group_id, allowed_ids) | |
152 | )) |
|
158 | )) | |
153 |
|
159 | |||
154 | if name_contains: |
|
160 | if name_contains: | |
155 | ilike_expression = u'%{}%'.format(safe_unicode(name_contains)) |
|
161 | ilike_expression = u'%{}%'.format(safe_unicode(name_contains)) | |
156 | query = query.filter( |
|
162 | query = query.filter( | |
157 | RepoGroup.group_name.ilike(ilike_expression)) |
|
163 | RepoGroup.group_name.ilike(ilike_expression)) | |
158 | query = query.limit(limit) |
|
164 | query = query.limit(limit) | |
159 |
|
165 | |||
160 |
acl_ |
|
166 | acl_iter = query | |
161 |
|
167 | |||
162 | return [ |
|
168 | return [ | |
163 | { |
|
169 | { | |
164 | 'id': obj.group_name, |
|
170 | 'id': obj.group_name, | |
165 |
' |
|
171 | 'value': org_query, | |
166 |
' |
|
172 | 'value_display': obj.group_name, | |
167 |
' |
|
173 | 'type': 'repo_group', | |
168 | 'url': h.route_path( |
|
174 | 'url': h.route_path( | |
169 | 'repo_group_home', repo_group_name=obj.group_name) |
|
175 | 'repo_group_home', repo_group_name=obj.group_name) | |
170 | } |
|
176 | } | |
171 |
for obj in acl_ |
|
177 | for obj in acl_iter] | |
|
178 | ||||
|
179 | def _get_user_list(self, name_contains=None, limit=20): | |||
|
180 | org_query = name_contains | |||
|
181 | if not name_contains: | |||
|
182 | return [] | |||
|
183 | ||||
|
184 | name_contains = re.compile('(?:user:)(.+)').findall(name_contains) | |||
|
185 | if len(name_contains) != 1: | |||
|
186 | return [] | |||
|
187 | name_contains = name_contains[0] | |||
|
188 | ||||
|
189 | query = User.query()\ | |||
|
190 | .order_by(func.length(User.username))\ | |||
|
191 | .order_by(User.username) \ | |||
|
192 | .filter(User.username != User.DEFAULT_USER) | |||
172 |
|
193 | |||
173 | def _get_hash_commit_list(self, auth_user, query=None): |
|
194 | if name_contains: | |
|
195 | ilike_expression = u'%{}%'.format(safe_unicode(name_contains)) | |||
|
196 | query = query.filter( | |||
|
197 | User.username.ilike(ilike_expression)) | |||
|
198 | query = query.limit(limit) | |||
|
199 | ||||
|
200 | acl_iter = query | |||
|
201 | ||||
|
202 | return [ | |||
|
203 | { | |||
|
204 | 'id': obj.user_id, | |||
|
205 | 'value': org_query, | |||
|
206 | 'value_display': obj.username, | |||
|
207 | 'type': 'user', | |||
|
208 | 'icon_link': h.gravatar_url(obj.email, 30), | |||
|
209 | 'url': h.route_path( | |||
|
210 | 'user_profile', username=obj.username) | |||
|
211 | } | |||
|
212 | for obj in acl_iter] | |||
|
213 | ||||
|
214 | def _get_hash_commit_list(self, auth_user, query): | |||
|
215 | org_query = query | |||
174 | if not query or len(query) < 3: |
|
216 | if not query or len(query) < 3: | |
175 | return [] |
|
217 | return [] | |
176 |
|
218 | |||
177 | commit_hashes = re.compile('(?:commit:)([0-9a-f]{2,40})').findall(query) |
|
219 | commit_hashes = re.compile('(?:commit:)([0-9a-f]{2,40})').findall(query) | |
178 |
|
220 | |||
179 | if len(commit_hashes) != 1: |
|
221 | if len(commit_hashes) != 1: | |
180 | return [] |
|
222 | return [] | |
181 |
|
223 | commit_hash = commit_hashes[0] | ||
182 | commit_hash_prefix = commit_hashes[0] |
|
|||
183 |
|
224 | |||
184 | searcher = searcher_from_config(self.request.registry.settings) |
|
225 | searcher = searcher_from_config(self.request.registry.settings) | |
185 | result = searcher.search( |
|
226 | result = searcher.search( | |
186 |
'commit_id:%s*' % commit_hash |
|
227 | 'commit_id:%s*' % commit_hash, 'commit', auth_user, | |
187 | raise_on_exc=False) |
|
228 | raise_on_exc=False) | |
188 |
|
229 | |||
189 | return [ |
|
230 | return [ | |
190 | { |
|
231 | { | |
191 | 'id': entry['commit_id'], |
|
232 | 'id': entry['commit_id'], | |
192 |
' |
|
233 | 'value': org_query, | |
|
234 | 'value_display': 'repo `{}` commit: {}'.format( | |||
|
235 | entry['repository'], entry['commit_id']), | |||
193 | 'type': 'commit', |
|
236 | 'type': 'commit', | |
194 |
|
|
237 | 'repo': entry['repository'], | |
195 | 'url': h.route_path( |
|
238 | 'url': h.route_path( | |
196 | 'repo_commit', |
|
239 | 'repo_commit', | |
197 | repo_name=entry['repository'], commit_id=entry['commit_id']) |
|
240 | repo_name=entry['repository'], commit_id=entry['commit_id']) | |
198 | } |
|
241 | } | |
199 | for entry in result['results']] |
|
242 | for entry in result['results']] | |
200 |
|
243 | |||
201 | @LoginRequired() |
|
244 | @LoginRequired() | |
202 | @view_config( |
|
245 | @view_config( | |
203 | route_name='repo_list_data', request_method='GET', |
|
246 | route_name='repo_list_data', request_method='GET', | |
204 | renderer='json_ext', xhr=True) |
|
247 | renderer='json_ext', xhr=True) | |
205 | def repo_list_data(self): |
|
248 | def repo_list_data(self): | |
206 | _ = self.request.translate |
|
249 | _ = self.request.translate | |
207 | self.load_default_context() |
|
250 | self.load_default_context() | |
208 |
|
251 | |||
209 | query = self.request.GET.get('query') |
|
252 | query = self.request.GET.get('query') | |
210 | repo_type = self.request.GET.get('repo_type') |
|
253 | repo_type = self.request.GET.get('repo_type') | |
211 | log.debug('generating repo list, query:%s, repo_type:%s', |
|
254 | log.debug('generating repo list, query:%s, repo_type:%s', | |
212 | query, repo_type) |
|
255 | query, repo_type) | |
213 |
|
256 | |||
214 | res = [] |
|
257 | res = [] | |
215 | repos = self._get_repo_list(query, repo_type=repo_type) |
|
258 | repos = self._get_repo_list(query, repo_type=repo_type) | |
216 | if repos: |
|
259 | if repos: | |
217 | res.append({ |
|
260 | res.append({ | |
218 | 'text': _('Repositories'), |
|
261 | 'text': _('Repositories'), | |
219 | 'children': repos |
|
262 | 'children': repos | |
220 | }) |
|
263 | }) | |
221 |
|
264 | |||
222 | data = { |
|
265 | data = { | |
223 | 'more': False, |
|
266 | 'more': False, | |
224 | 'results': res |
|
267 | 'results': res | |
225 | } |
|
268 | } | |
226 | return data |
|
269 | return data | |
227 |
|
270 | |||
228 | @LoginRequired() |
|
271 | @LoginRequired() | |
229 | @view_config( |
|
272 | @view_config( | |
230 | route_name='goto_switcher_data', request_method='GET', |
|
273 | route_name='goto_switcher_data', request_method='GET', | |
231 | renderer='json_ext', xhr=True) |
|
274 | renderer='json_ext', xhr=True) | |
232 | def goto_switcher_data(self): |
|
275 | def goto_switcher_data(self): | |
233 | c = self.load_default_context() |
|
276 | c = self.load_default_context() | |
234 |
|
277 | |||
235 | _ = self.request.translate |
|
278 | _ = self.request.translate | |
236 |
|
279 | |||
237 | query = self.request.GET.get('query') |
|
280 | query = self.request.GET.get('query') | |
238 |
log.debug('generating |
|
281 | log.debug('generating main filter data, query %s', query) | |
239 |
|
282 | |||
|
283 | default_search_val = 'Full text search for: `{}`'.format(query) | |||
240 | res = [] |
|
284 | res = [] | |
241 | repo_groups = self._get_repo_group_list(query) |
|
285 | if not query: | |
242 | if repo_groups: |
|
286 | return {'suggestions': res} | |
|
287 | ||||
243 |
|
|
288 | res.append({ | |
244 | 'text': _('Groups'), |
|
289 | 'id': -1, | |
245 | 'children': repo_groups |
|
290 | 'value': query, | |
|
291 | 'value_display': default_search_val, | |||
|
292 | 'type': 'search', | |||
|
293 | 'url': h.route_path( | |||
|
294 | 'search', _query={'q': query}) | |||
246 |
|
|
295 | }) | |
247 |
|
296 | |||
|
297 | repo_groups = self._get_repo_group_list(query) | |||
|
298 | for serialized_repo_group in repo_groups: | |||
|
299 | res.append(serialized_repo_group) | |||
|
300 | ||||
248 | repos = self._get_repo_list(query) |
|
301 | repos = self._get_repo_list(query) | |
249 | if repos: |
|
302 | for serialized_repo in repos: | |
250 |
res.append( |
|
303 | res.append(serialized_repo) | |
251 | 'text': _('Repositories'), |
|
304 | ||
252 | 'children': repos |
|
305 | # TODO(marcink): permissions for that ? | |
253 | }) |
|
306 | users = self._get_user_list(query) | |
|
307 | for serialized_user in users: | |||
|
308 | res.append(serialized_user) | |||
254 |
|
309 | |||
255 | commits = self._get_hash_commit_list(c.auth_user, query) |
|
310 | commits = self._get_hash_commit_list(c.auth_user, query) | |
256 | if commits: |
|
311 | if commits: | |
257 |
unique_repos = |
|
312 | unique_repos = collections.OrderedDict() | |
258 | for commit in commits: |
|
313 | for commit in commits: | |
259 |
|
|
314 | repo_name = commit['repo'] | |
260 |
|
|
315 | unique_repos.setdefault(repo_name, []).append(commit) | |
261 |
|
316 | |||
262 | for repo in unique_repos: |
|
317 | for repo, commits in unique_repos.items(): | |
263 | res.append({ |
|
318 | for commit in commits: | |
264 | 'text': _('Commits in %(repo)s') % {'repo': repo}, |
|
319 | res.append(commit) | |
265 | 'children': unique_repos[repo] |
|
|||
266 | }) |
|
|||
267 |
|
320 | |||
268 | data = { |
|
321 | return {'suggestions': res} | |
269 | 'more': False, |
|
|||
270 | 'results': res |
|
|||
271 | } |
|
|||
272 | return data |
|
|||
273 |
|
322 | |||
274 | def _get_groups_and_repos(self, repo_group_id=None): |
|
323 | def _get_groups_and_repos(self, repo_group_id=None): | |
275 | # repo groups groups |
|
324 | # repo groups groups | |
276 | repo_group_list = RepoGroup.get_all_repo_groups(group_id=repo_group_id) |
|
325 | repo_group_list = RepoGroup.get_all_repo_groups(group_id=repo_group_id) | |
277 | _perms = ['group.read', 'group.write', 'group.admin'] |
|
326 | _perms = ['group.read', 'group.write', 'group.admin'] | |
278 | repo_group_list_acl = RepoGroupList(repo_group_list, perm_set=_perms) |
|
327 | repo_group_list_acl = RepoGroupList(repo_group_list, perm_set=_perms) | |
279 | repo_group_data = RepoGroupModel().get_repo_groups_as_dict( |
|
328 | repo_group_data = RepoGroupModel().get_repo_groups_as_dict( | |
280 | repo_group_list=repo_group_list_acl, admin=False) |
|
329 | repo_group_list=repo_group_list_acl, admin=False) | |
281 |
|
330 | |||
282 | # repositories |
|
331 | # repositories | |
283 | repo_list = Repository.get_all_repos(group_id=repo_group_id) |
|
332 | repo_list = Repository.get_all_repos(group_id=repo_group_id) | |
284 | _perms = ['repository.read', 'repository.write', 'repository.admin'] |
|
333 | _perms = ['repository.read', 'repository.write', 'repository.admin'] | |
285 | repo_list_acl = RepoList(repo_list, perm_set=_perms) |
|
334 | repo_list_acl = RepoList(repo_list, perm_set=_perms) | |
286 | repo_data = RepoModel().get_repos_as_dict( |
|
335 | repo_data = RepoModel().get_repos_as_dict( | |
287 | repo_list=repo_list_acl, admin=False) |
|
336 | repo_list=repo_list_acl, admin=False) | |
288 |
|
337 | |||
289 | return repo_data, repo_group_data |
|
338 | return repo_data, repo_group_data | |
290 |
|
339 | |||
291 | @LoginRequired() |
|
340 | @LoginRequired() | |
292 | @view_config( |
|
341 | @view_config( | |
293 | route_name='home', request_method='GET', |
|
342 | route_name='home', request_method='GET', | |
294 | renderer='rhodecode:templates/index.mako') |
|
343 | renderer='rhodecode:templates/index.mako') | |
295 | def main_page(self): |
|
344 | def main_page(self): | |
296 | c = self.load_default_context() |
|
345 | c = self.load_default_context() | |
297 | c.repo_group = None |
|
346 | c.repo_group = None | |
298 |
|
347 | |||
299 | repo_data, repo_group_data = self._get_groups_and_repos() |
|
348 | repo_data, repo_group_data = self._get_groups_and_repos() | |
300 | # json used to render the grids |
|
349 | # json used to render the grids | |
301 | c.repos_data = json.dumps(repo_data) |
|
350 | c.repos_data = json.dumps(repo_data) | |
302 | c.repo_groups_data = json.dumps(repo_group_data) |
|
351 | c.repo_groups_data = json.dumps(repo_group_data) | |
303 |
|
352 | |||
304 | return self._get_template_context(c) |
|
353 | return self._get_template_context(c) | |
305 |
|
354 | |||
306 | @LoginRequired() |
|
355 | @LoginRequired() | |
307 | @HasRepoGroupPermissionAnyDecorator( |
|
356 | @HasRepoGroupPermissionAnyDecorator( | |
308 | 'group.read', 'group.write', 'group.admin') |
|
357 | 'group.read', 'group.write', 'group.admin') | |
309 | @view_config( |
|
358 | @view_config( | |
310 | route_name='repo_group_home', request_method='GET', |
|
359 | route_name='repo_group_home', request_method='GET', | |
311 | renderer='rhodecode:templates/index_repo_group.mako') |
|
360 | renderer='rhodecode:templates/index_repo_group.mako') | |
312 | @view_config( |
|
361 | @view_config( | |
313 | route_name='repo_group_home_slash', request_method='GET', |
|
362 | route_name='repo_group_home_slash', request_method='GET', | |
314 | renderer='rhodecode:templates/index_repo_group.mako') |
|
363 | renderer='rhodecode:templates/index_repo_group.mako') | |
315 | def repo_group_main_page(self): |
|
364 | def repo_group_main_page(self): | |
316 | c = self.load_default_context() |
|
365 | c = self.load_default_context() | |
317 | c.repo_group = self.request.db_repo_group |
|
366 | c.repo_group = self.request.db_repo_group | |
318 | repo_data, repo_group_data = self._get_groups_and_repos( |
|
367 | repo_data, repo_group_data = self._get_groups_and_repos( | |
319 | c.repo_group.group_id) |
|
368 | c.repo_group.group_id) | |
320 |
|
369 | |||
321 | # json used to render the grids |
|
370 | # json used to render the grids | |
322 | c.repos_data = json.dumps(repo_data) |
|
371 | c.repos_data = json.dumps(repo_data) | |
323 | c.repo_groups_data = json.dumps(repo_group_data) |
|
372 | c.repo_groups_data = json.dumps(repo_group_data) | |
324 |
|
373 | |||
325 | return self._get_template_context(c) |
|
374 | return self._get_template_context(c) |
@@ -1,1298 +1,1301 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 |
|
2 | |||
3 | # Copyright (C) 2011-2018 RhodeCode GmbH |
|
3 | # Copyright (C) 2011-2018 RhodeCode GmbH | |
4 | # |
|
4 | # | |
5 | # This program is free software: you can redistribute it and/or modify |
|
5 | # This program is free software: you can redistribute it and/or modify | |
6 | # it under the terms of the GNU Affero General Public License, version 3 |
|
6 | # it under the terms of the GNU Affero General Public License, version 3 | |
7 | # (only), as published by the Free Software Foundation. |
|
7 | # (only), as published by the Free Software Foundation. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU Affero General Public License |
|
14 | # You should have received a copy of the GNU Affero General Public License | |
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | # |
|
16 | # | |
17 | # This program is dual-licensed. If you wish to learn more about the |
|
17 | # This program is dual-licensed. If you wish to learn more about the | |
18 | # RhodeCode Enterprise Edition, including its added features, Support services, |
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |
20 |
|
20 | |||
21 | import logging |
|
21 | import logging | |
22 | import collections |
|
22 | import collections | |
23 |
|
23 | |||
24 | import formencode |
|
24 | import formencode | |
25 | import formencode.htmlfill |
|
25 | import formencode.htmlfill | |
26 | import peppercorn |
|
26 | import peppercorn | |
27 | from pyramid.httpexceptions import ( |
|
27 | from pyramid.httpexceptions import ( | |
28 | HTTPFound, HTTPNotFound, HTTPForbidden, HTTPBadRequest) |
|
28 | HTTPFound, HTTPNotFound, HTTPForbidden, HTTPBadRequest) | |
29 | from pyramid.view import view_config |
|
29 | from pyramid.view import view_config | |
30 | from pyramid.renderers import render |
|
30 | from pyramid.renderers import render | |
31 |
|
31 | |||
32 | from rhodecode import events |
|
32 | from rhodecode import events | |
33 | from rhodecode.apps._base import RepoAppView, DataGridAppView |
|
33 | from rhodecode.apps._base import RepoAppView, DataGridAppView | |
34 |
|
34 | |||
35 | from rhodecode.lib import helpers as h, diffs, codeblocks, channelstream |
|
35 | from rhodecode.lib import helpers as h, diffs, codeblocks, channelstream | |
36 | from rhodecode.lib.base import vcs_operation_context |
|
36 | from rhodecode.lib.base import vcs_operation_context | |
37 | from rhodecode.lib.diffs import load_cached_diff, cache_diff, diff_cache_exist |
|
37 | from rhodecode.lib.diffs import load_cached_diff, cache_diff, diff_cache_exist | |
38 | from rhodecode.lib.ext_json import json |
|
38 | from rhodecode.lib.ext_json import json | |
39 | from rhodecode.lib.auth import ( |
|
39 | from rhodecode.lib.auth import ( | |
40 | LoginRequired, HasRepoPermissionAny, HasRepoPermissionAnyDecorator, |
|
40 | LoginRequired, HasRepoPermissionAny, HasRepoPermissionAnyDecorator, | |
41 | NotAnonymous, CSRFRequired) |
|
41 | NotAnonymous, CSRFRequired) | |
42 | from rhodecode.lib.utils2 import str2bool, safe_str, safe_unicode |
|
42 | from rhodecode.lib.utils2 import str2bool, safe_str, safe_unicode | |
43 | from rhodecode.lib.vcs.backends.base import EmptyCommit, UpdateFailureReason |
|
43 | from rhodecode.lib.vcs.backends.base import EmptyCommit, UpdateFailureReason | |
44 | from rhodecode.lib.vcs.exceptions import (CommitDoesNotExistError, |
|
44 | from rhodecode.lib.vcs.exceptions import (CommitDoesNotExistError, | |
45 | RepositoryRequirementError, EmptyRepositoryError) |
|
45 | RepositoryRequirementError, EmptyRepositoryError) | |
46 | from rhodecode.model.changeset_status import ChangesetStatusModel |
|
46 | from rhodecode.model.changeset_status import ChangesetStatusModel | |
47 | from rhodecode.model.comment import CommentsModel |
|
47 | from rhodecode.model.comment import CommentsModel | |
48 | from rhodecode.model.db import (func, or_, PullRequest, PullRequestVersion, |
|
48 | from rhodecode.model.db import (func, or_, PullRequest, PullRequestVersion, | |
49 | ChangesetComment, ChangesetStatus, Repository) |
|
49 | ChangesetComment, ChangesetStatus, Repository) | |
50 | from rhodecode.model.forms import PullRequestForm |
|
50 | from rhodecode.model.forms import PullRequestForm | |
51 | from rhodecode.model.meta import Session |
|
51 | from rhodecode.model.meta import Session | |
52 | from rhodecode.model.pull_request import PullRequestModel, MergeCheck |
|
52 | from rhodecode.model.pull_request import PullRequestModel, MergeCheck | |
53 | from rhodecode.model.scm import ScmModel |
|
53 | from rhodecode.model.scm import ScmModel | |
54 |
|
54 | |||
55 | log = logging.getLogger(__name__) |
|
55 | log = logging.getLogger(__name__) | |
56 |
|
56 | |||
57 |
|
57 | |||
58 | class RepoPullRequestsView(RepoAppView, DataGridAppView): |
|
58 | class RepoPullRequestsView(RepoAppView, DataGridAppView): | |
59 |
|
59 | |||
60 | def load_default_context(self): |
|
60 | def load_default_context(self): | |
61 | c = self._get_local_tmpl_context(include_app_defaults=True) |
|
61 | c = self._get_local_tmpl_context(include_app_defaults=True) | |
62 | c.REVIEW_STATUS_APPROVED = ChangesetStatus.STATUS_APPROVED |
|
62 | c.REVIEW_STATUS_APPROVED = ChangesetStatus.STATUS_APPROVED | |
63 | c.REVIEW_STATUS_REJECTED = ChangesetStatus.STATUS_REJECTED |
|
63 | c.REVIEW_STATUS_REJECTED = ChangesetStatus.STATUS_REJECTED | |
64 |
|
64 | |||
65 | return c |
|
65 | return c | |
66 |
|
66 | |||
67 | def _get_pull_requests_list( |
|
67 | def _get_pull_requests_list( | |
68 | self, repo_name, source, filter_type, opened_by, statuses): |
|
68 | self, repo_name, source, filter_type, opened_by, statuses): | |
69 |
|
69 | |||
70 | draw, start, limit = self._extract_chunk(self.request) |
|
70 | draw, start, limit = self._extract_chunk(self.request) | |
71 | search_q, order_by, order_dir = self._extract_ordering(self.request) |
|
71 | search_q, order_by, order_dir = self._extract_ordering(self.request) | |
72 | _render = self.request.get_partial_renderer( |
|
72 | _render = self.request.get_partial_renderer( | |
73 | 'rhodecode:templates/data_table/_dt_elements.mako') |
|
73 | 'rhodecode:templates/data_table/_dt_elements.mako') | |
74 |
|
74 | |||
75 | # pagination |
|
75 | # pagination | |
76 |
|
76 | |||
77 | if filter_type == 'awaiting_review': |
|
77 | if filter_type == 'awaiting_review': | |
78 | pull_requests = PullRequestModel().get_awaiting_review( |
|
78 | pull_requests = PullRequestModel().get_awaiting_review( | |
79 | repo_name, source=source, opened_by=opened_by, |
|
79 | repo_name, source=source, opened_by=opened_by, | |
80 | statuses=statuses, offset=start, length=limit, |
|
80 | statuses=statuses, offset=start, length=limit, | |
81 | order_by=order_by, order_dir=order_dir) |
|
81 | order_by=order_by, order_dir=order_dir) | |
82 | pull_requests_total_count = PullRequestModel().count_awaiting_review( |
|
82 | pull_requests_total_count = PullRequestModel().count_awaiting_review( | |
83 | repo_name, source=source, statuses=statuses, |
|
83 | repo_name, source=source, statuses=statuses, | |
84 | opened_by=opened_by) |
|
84 | opened_by=opened_by) | |
85 | elif filter_type == 'awaiting_my_review': |
|
85 | elif filter_type == 'awaiting_my_review': | |
86 | pull_requests = PullRequestModel().get_awaiting_my_review( |
|
86 | pull_requests = PullRequestModel().get_awaiting_my_review( | |
87 | repo_name, source=source, opened_by=opened_by, |
|
87 | repo_name, source=source, opened_by=opened_by, | |
88 | user_id=self._rhodecode_user.user_id, statuses=statuses, |
|
88 | user_id=self._rhodecode_user.user_id, statuses=statuses, | |
89 | offset=start, length=limit, order_by=order_by, |
|
89 | offset=start, length=limit, order_by=order_by, | |
90 | order_dir=order_dir) |
|
90 | order_dir=order_dir) | |
91 | pull_requests_total_count = PullRequestModel().count_awaiting_my_review( |
|
91 | pull_requests_total_count = PullRequestModel().count_awaiting_my_review( | |
92 | repo_name, source=source, user_id=self._rhodecode_user.user_id, |
|
92 | repo_name, source=source, user_id=self._rhodecode_user.user_id, | |
93 | statuses=statuses, opened_by=opened_by) |
|
93 | statuses=statuses, opened_by=opened_by) | |
94 | else: |
|
94 | else: | |
95 | pull_requests = PullRequestModel().get_all( |
|
95 | pull_requests = PullRequestModel().get_all( | |
96 | repo_name, source=source, opened_by=opened_by, |
|
96 | repo_name, source=source, opened_by=opened_by, | |
97 | statuses=statuses, offset=start, length=limit, |
|
97 | statuses=statuses, offset=start, length=limit, | |
98 | order_by=order_by, order_dir=order_dir) |
|
98 | order_by=order_by, order_dir=order_dir) | |
99 | pull_requests_total_count = PullRequestModel().count_all( |
|
99 | pull_requests_total_count = PullRequestModel().count_all( | |
100 | repo_name, source=source, statuses=statuses, |
|
100 | repo_name, source=source, statuses=statuses, | |
101 | opened_by=opened_by) |
|
101 | opened_by=opened_by) | |
102 |
|
102 | |||
103 | data = [] |
|
103 | data = [] | |
104 | comments_model = CommentsModel() |
|
104 | comments_model = CommentsModel() | |
105 | for pr in pull_requests: |
|
105 | for pr in pull_requests: | |
106 | comments = comments_model.get_all_comments( |
|
106 | comments = comments_model.get_all_comments( | |
107 | self.db_repo.repo_id, pull_request=pr) |
|
107 | self.db_repo.repo_id, pull_request=pr) | |
108 |
|
108 | |||
109 | data.append({ |
|
109 | data.append({ | |
110 | 'name': _render('pullrequest_name', |
|
110 | 'name': _render('pullrequest_name', | |
111 | pr.pull_request_id, pr.target_repo.repo_name), |
|
111 | pr.pull_request_id, pr.target_repo.repo_name), | |
112 | 'name_raw': pr.pull_request_id, |
|
112 | 'name_raw': pr.pull_request_id, | |
113 | 'status': _render('pullrequest_status', |
|
113 | 'status': _render('pullrequest_status', | |
114 | pr.calculated_review_status()), |
|
114 | pr.calculated_review_status()), | |
115 | 'title': _render( |
|
115 | 'title': _render( | |
116 | 'pullrequest_title', pr.title, pr.description), |
|
116 | 'pullrequest_title', pr.title, pr.description), | |
117 | 'description': h.escape(pr.description), |
|
117 | 'description': h.escape(pr.description), | |
118 | 'updated_on': _render('pullrequest_updated_on', |
|
118 | 'updated_on': _render('pullrequest_updated_on', | |
119 | h.datetime_to_time(pr.updated_on)), |
|
119 | h.datetime_to_time(pr.updated_on)), | |
120 | 'updated_on_raw': h.datetime_to_time(pr.updated_on), |
|
120 | 'updated_on_raw': h.datetime_to_time(pr.updated_on), | |
121 | 'created_on': _render('pullrequest_updated_on', |
|
121 | 'created_on': _render('pullrequest_updated_on', | |
122 | h.datetime_to_time(pr.created_on)), |
|
122 | h.datetime_to_time(pr.created_on)), | |
123 | 'created_on_raw': h.datetime_to_time(pr.created_on), |
|
123 | 'created_on_raw': h.datetime_to_time(pr.created_on), | |
124 | 'author': _render('pullrequest_author', |
|
124 | 'author': _render('pullrequest_author', | |
125 | pr.author.full_contact, ), |
|
125 | pr.author.full_contact, ), | |
126 | 'author_raw': pr.author.full_name, |
|
126 | 'author_raw': pr.author.full_name, | |
127 | 'comments': _render('pullrequest_comments', len(comments)), |
|
127 | 'comments': _render('pullrequest_comments', len(comments)), | |
128 | 'comments_raw': len(comments), |
|
128 | 'comments_raw': len(comments), | |
129 | 'closed': pr.is_closed(), |
|
129 | 'closed': pr.is_closed(), | |
130 | }) |
|
130 | }) | |
131 |
|
131 | |||
132 | data = ({ |
|
132 | data = ({ | |
133 | 'draw': draw, |
|
133 | 'draw': draw, | |
134 | 'data': data, |
|
134 | 'data': data, | |
135 | 'recordsTotal': pull_requests_total_count, |
|
135 | 'recordsTotal': pull_requests_total_count, | |
136 | 'recordsFiltered': pull_requests_total_count, |
|
136 | 'recordsFiltered': pull_requests_total_count, | |
137 | }) |
|
137 | }) | |
138 | return data |
|
138 | return data | |
139 |
|
139 | |||
140 | @LoginRequired() |
|
140 | @LoginRequired() | |
141 | @HasRepoPermissionAnyDecorator( |
|
141 | @HasRepoPermissionAnyDecorator( | |
142 | 'repository.read', 'repository.write', 'repository.admin') |
|
142 | 'repository.read', 'repository.write', 'repository.admin') | |
143 | @view_config( |
|
143 | @view_config( | |
144 | route_name='pullrequest_show_all', request_method='GET', |
|
144 | route_name='pullrequest_show_all', request_method='GET', | |
145 | renderer='rhodecode:templates/pullrequests/pullrequests.mako') |
|
145 | renderer='rhodecode:templates/pullrequests/pullrequests.mako') | |
146 | def pull_request_list(self): |
|
146 | def pull_request_list(self): | |
147 | c = self.load_default_context() |
|
147 | c = self.load_default_context() | |
148 |
|
148 | |||
149 | req_get = self.request.GET |
|
149 | req_get = self.request.GET | |
150 | c.source = str2bool(req_get.get('source')) |
|
150 | c.source = str2bool(req_get.get('source')) | |
151 | c.closed = str2bool(req_get.get('closed')) |
|
151 | c.closed = str2bool(req_get.get('closed')) | |
152 | c.my = str2bool(req_get.get('my')) |
|
152 | c.my = str2bool(req_get.get('my')) | |
153 | c.awaiting_review = str2bool(req_get.get('awaiting_review')) |
|
153 | c.awaiting_review = str2bool(req_get.get('awaiting_review')) | |
154 | c.awaiting_my_review = str2bool(req_get.get('awaiting_my_review')) |
|
154 | c.awaiting_my_review = str2bool(req_get.get('awaiting_my_review')) | |
155 |
|
155 | |||
156 | c.active = 'open' |
|
156 | c.active = 'open' | |
157 | if c.my: |
|
157 | if c.my: | |
158 | c.active = 'my' |
|
158 | c.active = 'my' | |
159 | if c.closed: |
|
159 | if c.closed: | |
160 | c.active = 'closed' |
|
160 | c.active = 'closed' | |
161 | if c.awaiting_review and not c.source: |
|
161 | if c.awaiting_review and not c.source: | |
162 | c.active = 'awaiting' |
|
162 | c.active = 'awaiting' | |
163 | if c.source and not c.awaiting_review: |
|
163 | if c.source and not c.awaiting_review: | |
164 | c.active = 'source' |
|
164 | c.active = 'source' | |
165 | if c.awaiting_my_review: |
|
165 | if c.awaiting_my_review: | |
166 | c.active = 'awaiting_my' |
|
166 | c.active = 'awaiting_my' | |
167 |
|
167 | |||
168 | return self._get_template_context(c) |
|
168 | return self._get_template_context(c) | |
169 |
|
169 | |||
170 | @LoginRequired() |
|
170 | @LoginRequired() | |
171 | @HasRepoPermissionAnyDecorator( |
|
171 | @HasRepoPermissionAnyDecorator( | |
172 | 'repository.read', 'repository.write', 'repository.admin') |
|
172 | 'repository.read', 'repository.write', 'repository.admin') | |
173 | @view_config( |
|
173 | @view_config( | |
174 | route_name='pullrequest_show_all_data', request_method='GET', |
|
174 | route_name='pullrequest_show_all_data', request_method='GET', | |
175 | renderer='json_ext', xhr=True) |
|
175 | renderer='json_ext', xhr=True) | |
176 | def pull_request_list_data(self): |
|
176 | def pull_request_list_data(self): | |
177 | self.load_default_context() |
|
177 | self.load_default_context() | |
178 |
|
178 | |||
179 | # additional filters |
|
179 | # additional filters | |
180 | req_get = self.request.GET |
|
180 | req_get = self.request.GET | |
181 | source = str2bool(req_get.get('source')) |
|
181 | source = str2bool(req_get.get('source')) | |
182 | closed = str2bool(req_get.get('closed')) |
|
182 | closed = str2bool(req_get.get('closed')) | |
183 | my = str2bool(req_get.get('my')) |
|
183 | my = str2bool(req_get.get('my')) | |
184 | awaiting_review = str2bool(req_get.get('awaiting_review')) |
|
184 | awaiting_review = str2bool(req_get.get('awaiting_review')) | |
185 | awaiting_my_review = str2bool(req_get.get('awaiting_my_review')) |
|
185 | awaiting_my_review = str2bool(req_get.get('awaiting_my_review')) | |
186 |
|
186 | |||
187 | filter_type = 'awaiting_review' if awaiting_review \ |
|
187 | filter_type = 'awaiting_review' if awaiting_review \ | |
188 | else 'awaiting_my_review' if awaiting_my_review \ |
|
188 | else 'awaiting_my_review' if awaiting_my_review \ | |
189 | else None |
|
189 | else None | |
190 |
|
190 | |||
191 | opened_by = None |
|
191 | opened_by = None | |
192 | if my: |
|
192 | if my: | |
193 | opened_by = [self._rhodecode_user.user_id] |
|
193 | opened_by = [self._rhodecode_user.user_id] | |
194 |
|
194 | |||
195 | statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN] |
|
195 | statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN] | |
196 | if closed: |
|
196 | if closed: | |
197 | statuses = [PullRequest.STATUS_CLOSED] |
|
197 | statuses = [PullRequest.STATUS_CLOSED] | |
198 |
|
198 | |||
199 | data = self._get_pull_requests_list( |
|
199 | data = self._get_pull_requests_list( | |
200 | repo_name=self.db_repo_name, source=source, |
|
200 | repo_name=self.db_repo_name, source=source, | |
201 | filter_type=filter_type, opened_by=opened_by, statuses=statuses) |
|
201 | filter_type=filter_type, opened_by=opened_by, statuses=statuses) | |
202 |
|
202 | |||
203 | return data |
|
203 | return data | |
204 |
|
204 | |||
205 | def _is_diff_cache_enabled(self, target_repo): |
|
205 | def _is_diff_cache_enabled(self, target_repo): | |
206 | caching_enabled = self._get_general_setting( |
|
206 | caching_enabled = self._get_general_setting( | |
207 | target_repo, 'rhodecode_diff_cache') |
|
207 | target_repo, 'rhodecode_diff_cache') | |
208 | log.debug('Diff caching enabled: %s', caching_enabled) |
|
208 | log.debug('Diff caching enabled: %s', caching_enabled) | |
209 | return caching_enabled |
|
209 | return caching_enabled | |
210 |
|
210 | |||
211 | def _get_diffset(self, source_repo_name, source_repo, |
|
211 | def _get_diffset(self, source_repo_name, source_repo, | |
212 | source_ref_id, target_ref_id, |
|
212 | source_ref_id, target_ref_id, | |
213 | target_commit, source_commit, diff_limit, file_limit, |
|
213 | target_commit, source_commit, diff_limit, file_limit, | |
214 | fulldiff): |
|
214 | fulldiff): | |
215 |
|
215 | |||
216 | vcs_diff = PullRequestModel().get_diff( |
|
216 | vcs_diff = PullRequestModel().get_diff( | |
217 | source_repo, source_ref_id, target_ref_id) |
|
217 | source_repo, source_ref_id, target_ref_id) | |
218 |
|
218 | |||
219 | diff_processor = diffs.DiffProcessor( |
|
219 | diff_processor = diffs.DiffProcessor( | |
220 | vcs_diff, format='newdiff', diff_limit=diff_limit, |
|
220 | vcs_diff, format='newdiff', diff_limit=diff_limit, | |
221 | file_limit=file_limit, show_full_diff=fulldiff) |
|
221 | file_limit=file_limit, show_full_diff=fulldiff) | |
222 |
|
222 | |||
223 | _parsed = diff_processor.prepare() |
|
223 | _parsed = diff_processor.prepare() | |
224 |
|
224 | |||
225 | diffset = codeblocks.DiffSet( |
|
225 | diffset = codeblocks.DiffSet( | |
226 | repo_name=self.db_repo_name, |
|
226 | repo_name=self.db_repo_name, | |
227 | source_repo_name=source_repo_name, |
|
227 | source_repo_name=source_repo_name, | |
228 | source_node_getter=codeblocks.diffset_node_getter(target_commit), |
|
228 | source_node_getter=codeblocks.diffset_node_getter(target_commit), | |
229 | target_node_getter=codeblocks.diffset_node_getter(source_commit), |
|
229 | target_node_getter=codeblocks.diffset_node_getter(source_commit), | |
230 | ) |
|
230 | ) | |
231 | diffset = self.path_filter.render_patchset_filtered( |
|
231 | diffset = self.path_filter.render_patchset_filtered( | |
232 | diffset, _parsed, target_commit.raw_id, source_commit.raw_id) |
|
232 | diffset, _parsed, target_commit.raw_id, source_commit.raw_id) | |
233 |
|
233 | |||
234 | return diffset |
|
234 | return diffset | |
235 |
|
235 | |||
236 | @LoginRequired() |
|
236 | @LoginRequired() | |
237 | @HasRepoPermissionAnyDecorator( |
|
237 | @HasRepoPermissionAnyDecorator( | |
238 | 'repository.read', 'repository.write', 'repository.admin') |
|
238 | 'repository.read', 'repository.write', 'repository.admin') | |
239 | @view_config( |
|
239 | @view_config( | |
240 | route_name='pullrequest_show', request_method='GET', |
|
240 | route_name='pullrequest_show', request_method='GET', | |
241 | renderer='rhodecode:templates/pullrequests/pullrequest_show.mako') |
|
241 | renderer='rhodecode:templates/pullrequests/pullrequest_show.mako') | |
242 | def pull_request_show(self): |
|
242 | def pull_request_show(self): | |
243 | pull_request_id = self.request.matchdict['pull_request_id'] |
|
243 | pull_request_id = self.request.matchdict['pull_request_id'] | |
244 |
|
244 | |||
245 | c = self.load_default_context() |
|
245 | c = self.load_default_context() | |
246 |
|
246 | |||
247 | version = self.request.GET.get('version') |
|
247 | version = self.request.GET.get('version') | |
248 | from_version = self.request.GET.get('from_version') or version |
|
248 | from_version = self.request.GET.get('from_version') or version | |
249 | merge_checks = self.request.GET.get('merge_checks') |
|
249 | merge_checks = self.request.GET.get('merge_checks') | |
250 | c.fulldiff = str2bool(self.request.GET.get('fulldiff')) |
|
250 | c.fulldiff = str2bool(self.request.GET.get('fulldiff')) | |
251 |
|
251 | |||
252 | (pull_request_latest, |
|
252 | (pull_request_latest, | |
253 | pull_request_at_ver, |
|
253 | pull_request_at_ver, | |
254 | pull_request_display_obj, |
|
254 | pull_request_display_obj, | |
255 | at_version) = PullRequestModel().get_pr_version( |
|
255 | at_version) = PullRequestModel().get_pr_version( | |
256 | pull_request_id, version=version) |
|
256 | pull_request_id, version=version) | |
257 | pr_closed = pull_request_latest.is_closed() |
|
257 | pr_closed = pull_request_latest.is_closed() | |
258 |
|
258 | |||
259 | if pr_closed and (version or from_version): |
|
259 | if pr_closed and (version or from_version): | |
260 | # not allow to browse versions |
|
260 | # not allow to browse versions | |
261 | raise HTTPFound(h.route_path( |
|
261 | raise HTTPFound(h.route_path( | |
262 | 'pullrequest_show', repo_name=self.db_repo_name, |
|
262 | 'pullrequest_show', repo_name=self.db_repo_name, | |
263 | pull_request_id=pull_request_id)) |
|
263 | pull_request_id=pull_request_id)) | |
264 |
|
264 | |||
265 | versions = pull_request_display_obj.versions() |
|
265 | versions = pull_request_display_obj.versions() | |
266 |
|
266 | |||
267 | c.at_version = at_version |
|
267 | c.at_version = at_version | |
268 | c.at_version_num = (at_version |
|
268 | c.at_version_num = (at_version | |
269 | if at_version and at_version != 'latest' |
|
269 | if at_version and at_version != 'latest' | |
270 | else None) |
|
270 | else None) | |
271 | c.at_version_pos = ChangesetComment.get_index_from_version( |
|
271 | c.at_version_pos = ChangesetComment.get_index_from_version( | |
272 | c.at_version_num, versions) |
|
272 | c.at_version_num, versions) | |
273 |
|
273 | |||
274 | (prev_pull_request_latest, |
|
274 | (prev_pull_request_latest, | |
275 | prev_pull_request_at_ver, |
|
275 | prev_pull_request_at_ver, | |
276 | prev_pull_request_display_obj, |
|
276 | prev_pull_request_display_obj, | |
277 | prev_at_version) = PullRequestModel().get_pr_version( |
|
277 | prev_at_version) = PullRequestModel().get_pr_version( | |
278 | pull_request_id, version=from_version) |
|
278 | pull_request_id, version=from_version) | |
279 |
|
279 | |||
280 | c.from_version = prev_at_version |
|
280 | c.from_version = prev_at_version | |
281 | c.from_version_num = (prev_at_version |
|
281 | c.from_version_num = (prev_at_version | |
282 | if prev_at_version and prev_at_version != 'latest' |
|
282 | if prev_at_version and prev_at_version != 'latest' | |
283 | else None) |
|
283 | else None) | |
284 | c.from_version_pos = ChangesetComment.get_index_from_version( |
|
284 | c.from_version_pos = ChangesetComment.get_index_from_version( | |
285 | c.from_version_num, versions) |
|
285 | c.from_version_num, versions) | |
286 |
|
286 | |||
287 | # define if we're in COMPARE mode or VIEW at version mode |
|
287 | # define if we're in COMPARE mode or VIEW at version mode | |
288 | compare = at_version != prev_at_version |
|
288 | compare = at_version != prev_at_version | |
289 |
|
289 | |||
290 | # pull_requests repo_name we opened it against |
|
290 | # pull_requests repo_name we opened it against | |
291 | # ie. target_repo must match |
|
291 | # ie. target_repo must match | |
292 | if self.db_repo_name != pull_request_at_ver.target_repo.repo_name: |
|
292 | if self.db_repo_name != pull_request_at_ver.target_repo.repo_name: | |
293 | raise HTTPNotFound() |
|
293 | raise HTTPNotFound() | |
294 |
|
294 | |||
295 | c.shadow_clone_url = PullRequestModel().get_shadow_clone_url( |
|
295 | c.shadow_clone_url = PullRequestModel().get_shadow_clone_url( | |
296 | pull_request_at_ver) |
|
296 | pull_request_at_ver) | |
297 |
|
297 | |||
298 | c.pull_request = pull_request_display_obj |
|
298 | c.pull_request = pull_request_display_obj | |
299 | c.pull_request_latest = pull_request_latest |
|
299 | c.pull_request_latest = pull_request_latest | |
300 |
|
300 | |||
301 | if compare or (at_version and not at_version == 'latest'): |
|
301 | if compare or (at_version and not at_version == 'latest'): | |
302 | c.allowed_to_change_status = False |
|
302 | c.allowed_to_change_status = False | |
303 | c.allowed_to_update = False |
|
303 | c.allowed_to_update = False | |
304 | c.allowed_to_merge = False |
|
304 | c.allowed_to_merge = False | |
305 | c.allowed_to_delete = False |
|
305 | c.allowed_to_delete = False | |
306 | c.allowed_to_comment = False |
|
306 | c.allowed_to_comment = False | |
307 | c.allowed_to_close = False |
|
307 | c.allowed_to_close = False | |
308 | else: |
|
308 | else: | |
309 | can_change_status = PullRequestModel().check_user_change_status( |
|
309 | can_change_status = PullRequestModel().check_user_change_status( | |
310 | pull_request_at_ver, self._rhodecode_user) |
|
310 | pull_request_at_ver, self._rhodecode_user) | |
311 | c.allowed_to_change_status = can_change_status and not pr_closed |
|
311 | c.allowed_to_change_status = can_change_status and not pr_closed | |
312 |
|
312 | |||
313 | c.allowed_to_update = PullRequestModel().check_user_update( |
|
313 | c.allowed_to_update = PullRequestModel().check_user_update( | |
314 | pull_request_latest, self._rhodecode_user) and not pr_closed |
|
314 | pull_request_latest, self._rhodecode_user) and not pr_closed | |
315 | c.allowed_to_merge = PullRequestModel().check_user_merge( |
|
315 | c.allowed_to_merge = PullRequestModel().check_user_merge( | |
316 | pull_request_latest, self._rhodecode_user) and not pr_closed |
|
316 | pull_request_latest, self._rhodecode_user) and not pr_closed | |
317 | c.allowed_to_delete = PullRequestModel().check_user_delete( |
|
317 | c.allowed_to_delete = PullRequestModel().check_user_delete( | |
318 | pull_request_latest, self._rhodecode_user) and not pr_closed |
|
318 | pull_request_latest, self._rhodecode_user) and not pr_closed | |
319 | c.allowed_to_comment = not pr_closed |
|
319 | c.allowed_to_comment = not pr_closed | |
320 | c.allowed_to_close = c.allowed_to_merge and not pr_closed |
|
320 | c.allowed_to_close = c.allowed_to_merge and not pr_closed | |
321 |
|
321 | |||
322 | c.forbid_adding_reviewers = False |
|
322 | c.forbid_adding_reviewers = False | |
323 | c.forbid_author_to_review = False |
|
323 | c.forbid_author_to_review = False | |
324 | c.forbid_commit_author_to_review = False |
|
324 | c.forbid_commit_author_to_review = False | |
325 |
|
325 | |||
326 | if pull_request_latest.reviewer_data and \ |
|
326 | if pull_request_latest.reviewer_data and \ | |
327 | 'rules' in pull_request_latest.reviewer_data: |
|
327 | 'rules' in pull_request_latest.reviewer_data: | |
328 | rules = pull_request_latest.reviewer_data['rules'] or {} |
|
328 | rules = pull_request_latest.reviewer_data['rules'] or {} | |
329 | try: |
|
329 | try: | |
330 | c.forbid_adding_reviewers = rules.get( |
|
330 | c.forbid_adding_reviewers = rules.get( | |
331 | 'forbid_adding_reviewers') |
|
331 | 'forbid_adding_reviewers') | |
332 | c.forbid_author_to_review = rules.get( |
|
332 | c.forbid_author_to_review = rules.get( | |
333 | 'forbid_author_to_review') |
|
333 | 'forbid_author_to_review') | |
334 | c.forbid_commit_author_to_review = rules.get( |
|
334 | c.forbid_commit_author_to_review = rules.get( | |
335 | 'forbid_commit_author_to_review') |
|
335 | 'forbid_commit_author_to_review') | |
336 | except Exception: |
|
336 | except Exception: | |
337 | pass |
|
337 | pass | |
338 |
|
338 | |||
339 | # check merge capabilities |
|
339 | # check merge capabilities | |
340 | _merge_check = MergeCheck.validate( |
|
340 | _merge_check = MergeCheck.validate( | |
341 | pull_request_latest, user=self._rhodecode_user, |
|
341 | pull_request_latest, user=self._rhodecode_user, | |
342 | translator=self.request.translate) |
|
342 | translator=self.request.translate) | |
343 | c.pr_merge_errors = _merge_check.error_details |
|
343 | c.pr_merge_errors = _merge_check.error_details | |
344 | c.pr_merge_possible = not _merge_check.failed |
|
344 | c.pr_merge_possible = not _merge_check.failed | |
345 | c.pr_merge_message = _merge_check.merge_msg |
|
345 | c.pr_merge_message = _merge_check.merge_msg | |
346 |
|
346 | |||
347 | c.pr_merge_info = MergeCheck.get_merge_conditions( |
|
347 | c.pr_merge_info = MergeCheck.get_merge_conditions( | |
348 | pull_request_latest, translator=self.request.translate) |
|
348 | pull_request_latest, translator=self.request.translate) | |
349 |
|
349 | |||
350 | c.pull_request_review_status = _merge_check.review_status |
|
350 | c.pull_request_review_status = _merge_check.review_status | |
351 | if merge_checks: |
|
351 | if merge_checks: | |
352 | self.request.override_renderer = \ |
|
352 | self.request.override_renderer = \ | |
353 | 'rhodecode:templates/pullrequests/pullrequest_merge_checks.mako' |
|
353 | 'rhodecode:templates/pullrequests/pullrequest_merge_checks.mako' | |
354 | return self._get_template_context(c) |
|
354 | return self._get_template_context(c) | |
355 |
|
355 | |||
356 | comments_model = CommentsModel() |
|
356 | comments_model = CommentsModel() | |
357 |
|
357 | |||
358 | # reviewers and statuses |
|
358 | # reviewers and statuses | |
359 | c.pull_request_reviewers = pull_request_at_ver.reviewers_statuses() |
|
359 | c.pull_request_reviewers = pull_request_at_ver.reviewers_statuses() | |
360 | allowed_reviewers = [x[0].user_id for x in c.pull_request_reviewers] |
|
360 | allowed_reviewers = [x[0].user_id for x in c.pull_request_reviewers] | |
361 |
|
361 | |||
362 | # GENERAL COMMENTS with versions # |
|
362 | # GENERAL COMMENTS with versions # | |
363 | q = comments_model._all_general_comments_of_pull_request(pull_request_latest) |
|
363 | q = comments_model._all_general_comments_of_pull_request(pull_request_latest) | |
364 | q = q.order_by(ChangesetComment.comment_id.asc()) |
|
364 | q = q.order_by(ChangesetComment.comment_id.asc()) | |
365 | general_comments = q |
|
365 | general_comments = q | |
366 |
|
366 | |||
367 | # pick comments we want to render at current version |
|
367 | # pick comments we want to render at current version | |
368 | c.comment_versions = comments_model.aggregate_comments( |
|
368 | c.comment_versions = comments_model.aggregate_comments( | |
369 | general_comments, versions, c.at_version_num) |
|
369 | general_comments, versions, c.at_version_num) | |
370 | c.comments = c.comment_versions[c.at_version_num]['until'] |
|
370 | c.comments = c.comment_versions[c.at_version_num]['until'] | |
371 |
|
371 | |||
372 | # INLINE COMMENTS with versions # |
|
372 | # INLINE COMMENTS with versions # | |
373 | q = comments_model._all_inline_comments_of_pull_request(pull_request_latest) |
|
373 | q = comments_model._all_inline_comments_of_pull_request(pull_request_latest) | |
374 | q = q.order_by(ChangesetComment.comment_id.asc()) |
|
374 | q = q.order_by(ChangesetComment.comment_id.asc()) | |
375 | inline_comments = q |
|
375 | inline_comments = q | |
376 |
|
376 | |||
377 | c.inline_versions = comments_model.aggregate_comments( |
|
377 | c.inline_versions = comments_model.aggregate_comments( | |
378 | inline_comments, versions, c.at_version_num, inline=True) |
|
378 | inline_comments, versions, c.at_version_num, inline=True) | |
379 |
|
379 | |||
380 | # inject latest version |
|
380 | # inject latest version | |
381 | latest_ver = PullRequest.get_pr_display_object( |
|
381 | latest_ver = PullRequest.get_pr_display_object( | |
382 | pull_request_latest, pull_request_latest) |
|
382 | pull_request_latest, pull_request_latest) | |
383 |
|
383 | |||
384 | c.versions = versions + [latest_ver] |
|
384 | c.versions = versions + [latest_ver] | |
385 |
|
385 | |||
386 | # if we use version, then do not show later comments |
|
386 | # if we use version, then do not show later comments | |
387 | # than current version |
|
387 | # than current version | |
388 | display_inline_comments = collections.defaultdict( |
|
388 | display_inline_comments = collections.defaultdict( | |
389 | lambda: collections.defaultdict(list)) |
|
389 | lambda: collections.defaultdict(list)) | |
390 | for co in inline_comments: |
|
390 | for co in inline_comments: | |
391 | if c.at_version_num: |
|
391 | if c.at_version_num: | |
392 | # pick comments that are at least UPTO given version, so we |
|
392 | # pick comments that are at least UPTO given version, so we | |
393 | # don't render comments for higher version |
|
393 | # don't render comments for higher version | |
394 | should_render = co.pull_request_version_id and \ |
|
394 | should_render = co.pull_request_version_id and \ | |
395 | co.pull_request_version_id <= c.at_version_num |
|
395 | co.pull_request_version_id <= c.at_version_num | |
396 | else: |
|
396 | else: | |
397 | # showing all, for 'latest' |
|
397 | # showing all, for 'latest' | |
398 | should_render = True |
|
398 | should_render = True | |
399 |
|
399 | |||
400 | if should_render: |
|
400 | if should_render: | |
401 | display_inline_comments[co.f_path][co.line_no].append(co) |
|
401 | display_inline_comments[co.f_path][co.line_no].append(co) | |
402 |
|
402 | |||
403 | # load diff data into template context, if we use compare mode then |
|
403 | # load diff data into template context, if we use compare mode then | |
404 | # diff is calculated based on changes between versions of PR |
|
404 | # diff is calculated based on changes between versions of PR | |
405 |
|
405 | |||
406 | source_repo = pull_request_at_ver.source_repo |
|
406 | source_repo = pull_request_at_ver.source_repo | |
407 | source_ref_id = pull_request_at_ver.source_ref_parts.commit_id |
|
407 | source_ref_id = pull_request_at_ver.source_ref_parts.commit_id | |
408 |
|
408 | |||
409 | target_repo = pull_request_at_ver.target_repo |
|
409 | target_repo = pull_request_at_ver.target_repo | |
410 | target_ref_id = pull_request_at_ver.target_ref_parts.commit_id |
|
410 | target_ref_id = pull_request_at_ver.target_ref_parts.commit_id | |
411 |
|
411 | |||
412 | if compare: |
|
412 | if compare: | |
413 | # in compare switch the diff base to latest commit from prev version |
|
413 | # in compare switch the diff base to latest commit from prev version | |
414 | target_ref_id = prev_pull_request_display_obj.revisions[0] |
|
414 | target_ref_id = prev_pull_request_display_obj.revisions[0] | |
415 |
|
415 | |||
416 | # despite opening commits for bookmarks/branches/tags, we always |
|
416 | # despite opening commits for bookmarks/branches/tags, we always | |
417 | # convert this to rev to prevent changes after bookmark or branch change |
|
417 | # convert this to rev to prevent changes after bookmark or branch change | |
418 | c.source_ref_type = 'rev' |
|
418 | c.source_ref_type = 'rev' | |
419 | c.source_ref = source_ref_id |
|
419 | c.source_ref = source_ref_id | |
420 |
|
420 | |||
421 | c.target_ref_type = 'rev' |
|
421 | c.target_ref_type = 'rev' | |
422 | c.target_ref = target_ref_id |
|
422 | c.target_ref = target_ref_id | |
423 |
|
423 | |||
424 | c.source_repo = source_repo |
|
424 | c.source_repo = source_repo | |
425 | c.target_repo = target_repo |
|
425 | c.target_repo = target_repo | |
426 |
|
426 | |||
427 | c.commit_ranges = [] |
|
427 | c.commit_ranges = [] | |
428 | source_commit = EmptyCommit() |
|
428 | source_commit = EmptyCommit() | |
429 | target_commit = EmptyCommit() |
|
429 | target_commit = EmptyCommit() | |
430 | c.missing_requirements = False |
|
430 | c.missing_requirements = False | |
431 |
|
431 | |||
432 | source_scm = source_repo.scm_instance() |
|
432 | source_scm = source_repo.scm_instance() | |
433 | target_scm = target_repo.scm_instance() |
|
433 | target_scm = target_repo.scm_instance() | |
434 |
|
434 | |||
435 | # try first shadow repo, fallback to regular repo |
|
435 | # try first shadow repo, fallback to regular repo | |
436 | try: |
|
436 | try: | |
437 | commits_source_repo = pull_request_latest.get_shadow_repo() |
|
437 | commits_source_repo = pull_request_latest.get_shadow_repo() | |
438 | except Exception: |
|
438 | except Exception: | |
439 | log.debug('Failed to get shadow repo', exc_info=True) |
|
439 | log.debug('Failed to get shadow repo', exc_info=True) | |
440 | commits_source_repo = source_scm |
|
440 | commits_source_repo = source_scm | |
441 |
|
441 | |||
442 | c.commits_source_repo = commits_source_repo |
|
442 | c.commits_source_repo = commits_source_repo | |
443 | c.ancestor = None # set it to None, to hide it from PR view |
|
443 | c.ancestor = None # set it to None, to hide it from PR view | |
444 |
|
444 | |||
445 | # empty version means latest, so we keep this to prevent |
|
445 | # empty version means latest, so we keep this to prevent | |
446 | # double caching |
|
446 | # double caching | |
447 | version_normalized = version or 'latest' |
|
447 | version_normalized = version or 'latest' | |
448 | from_version_normalized = from_version or 'latest' |
|
448 | from_version_normalized = from_version or 'latest' | |
449 |
|
449 | |||
450 | cache_path = self.rhodecode_vcs_repo.get_create_shadow_cache_pr_path( |
|
450 | cache_path = self.rhodecode_vcs_repo.get_create_shadow_cache_pr_path( | |
451 | target_repo) |
|
451 | target_repo) | |
452 | cache_file_path = diff_cache_exist( |
|
452 | cache_file_path = diff_cache_exist( | |
453 | cache_path, 'pull_request', pull_request_id, version_normalized, |
|
453 | cache_path, 'pull_request', pull_request_id, version_normalized, | |
454 | from_version_normalized, source_ref_id, target_ref_id, c.fulldiff) |
|
454 | from_version_normalized, source_ref_id, target_ref_id, c.fulldiff) | |
455 |
|
455 | |||
456 | caching_enabled = self._is_diff_cache_enabled(c.target_repo) |
|
456 | caching_enabled = self._is_diff_cache_enabled(c.target_repo) | |
457 | force_recache = str2bool(self.request.GET.get('force_recache')) |
|
457 | force_recache = str2bool(self.request.GET.get('force_recache')) | |
458 |
|
458 | |||
459 | cached_diff = None |
|
459 | cached_diff = None | |
460 | if caching_enabled: |
|
460 | if caching_enabled: | |
461 | cached_diff = load_cached_diff(cache_file_path) |
|
461 | cached_diff = load_cached_diff(cache_file_path) | |
462 |
|
462 | |||
463 | has_proper_commit_cache = ( |
|
463 | has_proper_commit_cache = ( | |
464 | cached_diff and cached_diff.get('commits') |
|
464 | cached_diff and cached_diff.get('commits') | |
465 | and len(cached_diff.get('commits', [])) == 5 |
|
465 | and len(cached_diff.get('commits', [])) == 5 | |
466 | and cached_diff.get('commits')[0] |
|
466 | and cached_diff.get('commits')[0] | |
467 | and cached_diff.get('commits')[3]) |
|
467 | and cached_diff.get('commits')[3]) | |
468 | if not force_recache and has_proper_commit_cache: |
|
468 | if not force_recache and has_proper_commit_cache: | |
469 | diff_commit_cache = \ |
|
469 | diff_commit_cache = \ | |
470 | (ancestor_commit, commit_cache, missing_requirements, |
|
470 | (ancestor_commit, commit_cache, missing_requirements, | |
471 | source_commit, target_commit) = cached_diff['commits'] |
|
471 | source_commit, target_commit) = cached_diff['commits'] | |
472 | else: |
|
472 | else: | |
473 | diff_commit_cache = \ |
|
473 | diff_commit_cache = \ | |
474 | (ancestor_commit, commit_cache, missing_requirements, |
|
474 | (ancestor_commit, commit_cache, missing_requirements, | |
475 | source_commit, target_commit) = self.get_commits( |
|
475 | source_commit, target_commit) = self.get_commits( | |
476 | commits_source_repo, |
|
476 | commits_source_repo, | |
477 | pull_request_at_ver, |
|
477 | pull_request_at_ver, | |
478 | source_commit, |
|
478 | source_commit, | |
479 | source_ref_id, |
|
479 | source_ref_id, | |
480 | source_scm, |
|
480 | source_scm, | |
481 | target_commit, |
|
481 | target_commit, | |
482 | target_ref_id, |
|
482 | target_ref_id, | |
483 | target_scm) |
|
483 | target_scm) | |
484 |
|
484 | |||
485 | # register our commit range |
|
485 | # register our commit range | |
486 | for comm in commit_cache.values(): |
|
486 | for comm in commit_cache.values(): | |
487 | c.commit_ranges.append(comm) |
|
487 | c.commit_ranges.append(comm) | |
488 |
|
488 | |||
489 | c.missing_requirements = missing_requirements |
|
489 | c.missing_requirements = missing_requirements | |
490 | c.ancestor_commit = ancestor_commit |
|
490 | c.ancestor_commit = ancestor_commit | |
491 | c.statuses = source_repo.statuses( |
|
491 | c.statuses = source_repo.statuses( | |
492 | [x.raw_id for x in c.commit_ranges]) |
|
492 | [x.raw_id for x in c.commit_ranges]) | |
493 |
|
493 | |||
494 | # auto collapse if we have more than limit |
|
494 | # auto collapse if we have more than limit | |
495 | collapse_limit = diffs.DiffProcessor._collapse_commits_over |
|
495 | collapse_limit = diffs.DiffProcessor._collapse_commits_over | |
496 | c.collapse_all_commits = len(c.commit_ranges) > collapse_limit |
|
496 | c.collapse_all_commits = len(c.commit_ranges) > collapse_limit | |
497 | c.compare_mode = compare |
|
497 | c.compare_mode = compare | |
498 |
|
498 | |||
499 | # diff_limit is the old behavior, will cut off the whole diff |
|
499 | # diff_limit is the old behavior, will cut off the whole diff | |
500 | # if the limit is applied otherwise will just hide the |
|
500 | # if the limit is applied otherwise will just hide the | |
501 | # big files from the front-end |
|
501 | # big files from the front-end | |
502 | diff_limit = c.visual.cut_off_limit_diff |
|
502 | diff_limit = c.visual.cut_off_limit_diff | |
503 | file_limit = c.visual.cut_off_limit_file |
|
503 | file_limit = c.visual.cut_off_limit_file | |
504 |
|
504 | |||
505 | c.missing_commits = False |
|
505 | c.missing_commits = False | |
506 | if (c.missing_requirements |
|
506 | if (c.missing_requirements | |
507 | or isinstance(source_commit, EmptyCommit) |
|
507 | or isinstance(source_commit, EmptyCommit) | |
508 | or source_commit == target_commit): |
|
508 | or source_commit == target_commit): | |
509 |
|
509 | |||
510 | c.missing_commits = True |
|
510 | c.missing_commits = True | |
511 | else: |
|
511 | else: | |
512 | c.inline_comments = display_inline_comments |
|
512 | c.inline_comments = display_inline_comments | |
513 |
|
513 | |||
514 | has_proper_diff_cache = cached_diff and cached_diff.get('commits') |
|
514 | has_proper_diff_cache = cached_diff and cached_diff.get('commits') | |
515 | if not force_recache and has_proper_diff_cache: |
|
515 | if not force_recache and has_proper_diff_cache: | |
516 | c.diffset = cached_diff['diff'] |
|
516 | c.diffset = cached_diff['diff'] | |
517 | (ancestor_commit, commit_cache, missing_requirements, |
|
517 | (ancestor_commit, commit_cache, missing_requirements, | |
518 | source_commit, target_commit) = cached_diff['commits'] |
|
518 | source_commit, target_commit) = cached_diff['commits'] | |
519 | else: |
|
519 | else: | |
520 | c.diffset = self._get_diffset( |
|
520 | c.diffset = self._get_diffset( | |
521 | c.source_repo.repo_name, commits_source_repo, |
|
521 | c.source_repo.repo_name, commits_source_repo, | |
522 | source_ref_id, target_ref_id, |
|
522 | source_ref_id, target_ref_id, | |
523 | target_commit, source_commit, |
|
523 | target_commit, source_commit, | |
524 | diff_limit, file_limit, c.fulldiff) |
|
524 | diff_limit, file_limit, c.fulldiff) | |
525 |
|
525 | |||
526 | # save cached diff |
|
526 | # save cached diff | |
527 | if caching_enabled: |
|
527 | if caching_enabled: | |
528 | cache_diff(cache_file_path, c.diffset, diff_commit_cache) |
|
528 | cache_diff(cache_file_path, c.diffset, diff_commit_cache) | |
529 |
|
529 | |||
530 | c.limited_diff = c.diffset.limited_diff |
|
530 | c.limited_diff = c.diffset.limited_diff | |
531 |
|
531 | |||
532 | # calculate removed files that are bound to comments |
|
532 | # calculate removed files that are bound to comments | |
533 | comment_deleted_files = [ |
|
533 | comment_deleted_files = [ | |
534 | fname for fname in display_inline_comments |
|
534 | fname for fname in display_inline_comments | |
535 | if fname not in c.diffset.file_stats] |
|
535 | if fname not in c.diffset.file_stats] | |
536 |
|
536 | |||
537 | c.deleted_files_comments = collections.defaultdict(dict) |
|
537 | c.deleted_files_comments = collections.defaultdict(dict) | |
538 | for fname, per_line_comments in display_inline_comments.items(): |
|
538 | for fname, per_line_comments in display_inline_comments.items(): | |
539 | if fname in comment_deleted_files: |
|
539 | if fname in comment_deleted_files: | |
540 | c.deleted_files_comments[fname]['stats'] = 0 |
|
540 | c.deleted_files_comments[fname]['stats'] = 0 | |
541 | c.deleted_files_comments[fname]['comments'] = list() |
|
541 | c.deleted_files_comments[fname]['comments'] = list() | |
542 | for lno, comments in per_line_comments.items(): |
|
542 | for lno, comments in per_line_comments.items(): | |
543 | c.deleted_files_comments[fname]['comments'].extend( |
|
543 | c.deleted_files_comments[fname]['comments'].extend( | |
544 | comments) |
|
544 | comments) | |
545 |
|
545 | |||
546 | # this is a hack to properly display links, when creating PR, the |
|
546 | # this is a hack to properly display links, when creating PR, the | |
547 | # compare view and others uses different notation, and |
|
547 | # compare view and others uses different notation, and | |
548 | # compare_commits.mako renders links based on the target_repo. |
|
548 | # compare_commits.mako renders links based on the target_repo. | |
549 | # We need to swap that here to generate it properly on the html side |
|
549 | # We need to swap that here to generate it properly on the html side | |
550 | c.target_repo = c.source_repo |
|
550 | c.target_repo = c.source_repo | |
551 |
|
551 | |||
552 | c.commit_statuses = ChangesetStatus.STATUSES |
|
552 | c.commit_statuses = ChangesetStatus.STATUSES | |
553 |
|
553 | |||
554 | c.show_version_changes = not pr_closed |
|
554 | c.show_version_changes = not pr_closed | |
555 | if c.show_version_changes: |
|
555 | if c.show_version_changes: | |
556 | cur_obj = pull_request_at_ver |
|
556 | cur_obj = pull_request_at_ver | |
557 | prev_obj = prev_pull_request_at_ver |
|
557 | prev_obj = prev_pull_request_at_ver | |
558 |
|
558 | |||
559 | old_commit_ids = prev_obj.revisions |
|
559 | old_commit_ids = prev_obj.revisions | |
560 | new_commit_ids = cur_obj.revisions |
|
560 | new_commit_ids = cur_obj.revisions | |
561 | commit_changes = PullRequestModel()._calculate_commit_id_changes( |
|
561 | commit_changes = PullRequestModel()._calculate_commit_id_changes( | |
562 | old_commit_ids, new_commit_ids) |
|
562 | old_commit_ids, new_commit_ids) | |
563 | c.commit_changes_summary = commit_changes |
|
563 | c.commit_changes_summary = commit_changes | |
564 |
|
564 | |||
565 | # calculate the diff for commits between versions |
|
565 | # calculate the diff for commits between versions | |
566 | c.commit_changes = [] |
|
566 | c.commit_changes = [] | |
567 | mark = lambda cs, fw: list( |
|
567 | mark = lambda cs, fw: list( | |
568 | h.itertools.izip_longest([], cs, fillvalue=fw)) |
|
568 | h.itertools.izip_longest([], cs, fillvalue=fw)) | |
569 | for c_type, raw_id in mark(commit_changes.added, 'a') \ |
|
569 | for c_type, raw_id in mark(commit_changes.added, 'a') \ | |
570 | + mark(commit_changes.removed, 'r') \ |
|
570 | + mark(commit_changes.removed, 'r') \ | |
571 | + mark(commit_changes.common, 'c'): |
|
571 | + mark(commit_changes.common, 'c'): | |
572 |
|
572 | |||
573 | if raw_id in commit_cache: |
|
573 | if raw_id in commit_cache: | |
574 | commit = commit_cache[raw_id] |
|
574 | commit = commit_cache[raw_id] | |
575 | else: |
|
575 | else: | |
576 | try: |
|
576 | try: | |
577 | commit = commits_source_repo.get_commit(raw_id) |
|
577 | commit = commits_source_repo.get_commit(raw_id) | |
578 | except CommitDoesNotExistError: |
|
578 | except CommitDoesNotExistError: | |
579 | # in case we fail extracting still use "dummy" commit |
|
579 | # in case we fail extracting still use "dummy" commit | |
580 | # for display in commit diff |
|
580 | # for display in commit diff | |
581 | commit = h.AttributeDict( |
|
581 | commit = h.AttributeDict( | |
582 | {'raw_id': raw_id, |
|
582 | {'raw_id': raw_id, | |
583 | 'message': 'EMPTY or MISSING COMMIT'}) |
|
583 | 'message': 'EMPTY or MISSING COMMIT'}) | |
584 | c.commit_changes.append([c_type, commit]) |
|
584 | c.commit_changes.append([c_type, commit]) | |
585 |
|
585 | |||
586 | # current user review statuses for each version |
|
586 | # current user review statuses for each version | |
587 | c.review_versions = {} |
|
587 | c.review_versions = {} | |
588 | if self._rhodecode_user.user_id in allowed_reviewers: |
|
588 | if self._rhodecode_user.user_id in allowed_reviewers: | |
589 | for co in general_comments: |
|
589 | for co in general_comments: | |
590 | if co.author.user_id == self._rhodecode_user.user_id: |
|
590 | if co.author.user_id == self._rhodecode_user.user_id: | |
591 | status = co.status_change |
|
591 | status = co.status_change | |
592 | if status: |
|
592 | if status: | |
593 | _ver_pr = status[0].comment.pull_request_version_id |
|
593 | _ver_pr = status[0].comment.pull_request_version_id | |
594 | c.review_versions[_ver_pr] = status[0] |
|
594 | c.review_versions[_ver_pr] = status[0] | |
595 |
|
595 | |||
596 | return self._get_template_context(c) |
|
596 | return self._get_template_context(c) | |
597 |
|
597 | |||
598 | def get_commits( |
|
598 | def get_commits( | |
599 | self, commits_source_repo, pull_request_at_ver, source_commit, |
|
599 | self, commits_source_repo, pull_request_at_ver, source_commit, | |
600 | source_ref_id, source_scm, target_commit, target_ref_id, target_scm): |
|
600 | source_ref_id, source_scm, target_commit, target_ref_id, target_scm): | |
601 | commit_cache = collections.OrderedDict() |
|
601 | commit_cache = collections.OrderedDict() | |
602 | missing_requirements = False |
|
602 | missing_requirements = False | |
603 | try: |
|
603 | try: | |
604 | pre_load = ["author", "branch", "date", "message"] |
|
604 | pre_load = ["author", "branch", "date", "message"] | |
605 | show_revs = pull_request_at_ver.revisions |
|
605 | show_revs = pull_request_at_ver.revisions | |
606 | for rev in show_revs: |
|
606 | for rev in show_revs: | |
607 | comm = commits_source_repo.get_commit( |
|
607 | comm = commits_source_repo.get_commit( | |
608 | commit_id=rev, pre_load=pre_load) |
|
608 | commit_id=rev, pre_load=pre_load) | |
609 | commit_cache[comm.raw_id] = comm |
|
609 | commit_cache[comm.raw_id] = comm | |
610 |
|
610 | |||
611 | # Order here matters, we first need to get target, and then |
|
611 | # Order here matters, we first need to get target, and then | |
612 | # the source |
|
612 | # the source | |
613 | target_commit = commits_source_repo.get_commit( |
|
613 | target_commit = commits_source_repo.get_commit( | |
614 | commit_id=safe_str(target_ref_id)) |
|
614 | commit_id=safe_str(target_ref_id)) | |
615 |
|
615 | |||
616 | source_commit = commits_source_repo.get_commit( |
|
616 | source_commit = commits_source_repo.get_commit( | |
617 | commit_id=safe_str(source_ref_id)) |
|
617 | commit_id=safe_str(source_ref_id)) | |
618 | except CommitDoesNotExistError: |
|
618 | except CommitDoesNotExistError: | |
619 | log.warning( |
|
619 | log.warning( | |
620 | 'Failed to get commit from `{}` repo'.format( |
|
620 | 'Failed to get commit from `{}` repo'.format( | |
621 | commits_source_repo), exc_info=True) |
|
621 | commits_source_repo), exc_info=True) | |
622 | except RepositoryRequirementError: |
|
622 | except RepositoryRequirementError: | |
623 | log.warning( |
|
623 | log.warning( | |
624 | 'Failed to get all required data from repo', exc_info=True) |
|
624 | 'Failed to get all required data from repo', exc_info=True) | |
625 | missing_requirements = True |
|
625 | missing_requirements = True | |
626 | ancestor_commit = None |
|
626 | ancestor_commit = None | |
627 | try: |
|
627 | try: | |
628 | ancestor_id = source_scm.get_common_ancestor( |
|
628 | ancestor_id = source_scm.get_common_ancestor( | |
629 | source_commit.raw_id, target_commit.raw_id, target_scm) |
|
629 | source_commit.raw_id, target_commit.raw_id, target_scm) | |
630 | ancestor_commit = source_scm.get_commit(ancestor_id) |
|
630 | ancestor_commit = source_scm.get_commit(ancestor_id) | |
631 | except Exception: |
|
631 | except Exception: | |
632 | ancestor_commit = None |
|
632 | ancestor_commit = None | |
633 | return ancestor_commit, commit_cache, missing_requirements, source_commit, target_commit |
|
633 | return ancestor_commit, commit_cache, missing_requirements, source_commit, target_commit | |
634 |
|
634 | |||
635 | def assure_not_empty_repo(self): |
|
635 | def assure_not_empty_repo(self): | |
636 | _ = self.request.translate |
|
636 | _ = self.request.translate | |
637 |
|
637 | |||
638 | try: |
|
638 | try: | |
639 | self.db_repo.scm_instance().get_commit() |
|
639 | self.db_repo.scm_instance().get_commit() | |
640 | except EmptyRepositoryError: |
|
640 | except EmptyRepositoryError: | |
641 | h.flash(h.literal(_('There are no commits yet')), |
|
641 | h.flash(h.literal(_('There are no commits yet')), | |
642 | category='warning') |
|
642 | category='warning') | |
643 | raise HTTPFound( |
|
643 | raise HTTPFound( | |
644 | h.route_path('repo_summary', repo_name=self.db_repo.repo_name)) |
|
644 | h.route_path('repo_summary', repo_name=self.db_repo.repo_name)) | |
645 |
|
645 | |||
646 | @LoginRequired() |
|
646 | @LoginRequired() | |
647 | @NotAnonymous() |
|
647 | @NotAnonymous() | |
648 | @HasRepoPermissionAnyDecorator( |
|
648 | @HasRepoPermissionAnyDecorator( | |
649 | 'repository.read', 'repository.write', 'repository.admin') |
|
649 | 'repository.read', 'repository.write', 'repository.admin') | |
650 | @view_config( |
|
650 | @view_config( | |
651 | route_name='pullrequest_new', request_method='GET', |
|
651 | route_name='pullrequest_new', request_method='GET', | |
652 | renderer='rhodecode:templates/pullrequests/pullrequest.mako') |
|
652 | renderer='rhodecode:templates/pullrequests/pullrequest.mako') | |
653 | def pull_request_new(self): |
|
653 | def pull_request_new(self): | |
654 | _ = self.request.translate |
|
654 | _ = self.request.translate | |
655 | c = self.load_default_context() |
|
655 | c = self.load_default_context() | |
656 |
|
656 | |||
657 | self.assure_not_empty_repo() |
|
657 | self.assure_not_empty_repo() | |
658 | source_repo = self.db_repo |
|
658 | source_repo = self.db_repo | |
659 |
|
659 | |||
660 | commit_id = self.request.GET.get('commit') |
|
660 | commit_id = self.request.GET.get('commit') | |
661 | branch_ref = self.request.GET.get('branch') |
|
661 | branch_ref = self.request.GET.get('branch') | |
662 | bookmark_ref = self.request.GET.get('bookmark') |
|
662 | bookmark_ref = self.request.GET.get('bookmark') | |
663 |
|
663 | |||
664 | try: |
|
664 | try: | |
665 | source_repo_data = PullRequestModel().generate_repo_data( |
|
665 | source_repo_data = PullRequestModel().generate_repo_data( | |
666 | source_repo, commit_id=commit_id, |
|
666 | source_repo, commit_id=commit_id, | |
667 | branch=branch_ref, bookmark=bookmark_ref, |
|
667 | branch=branch_ref, bookmark=bookmark_ref, | |
668 | translator=self.request.translate) |
|
668 | translator=self.request.translate) | |
669 | except CommitDoesNotExistError as e: |
|
669 | except CommitDoesNotExistError as e: | |
670 | log.exception(e) |
|
670 | log.exception(e) | |
671 | h.flash(_('Commit does not exist'), 'error') |
|
671 | h.flash(_('Commit does not exist'), 'error') | |
672 | raise HTTPFound( |
|
672 | raise HTTPFound( | |
673 | h.route_path('pullrequest_new', repo_name=source_repo.repo_name)) |
|
673 | h.route_path('pullrequest_new', repo_name=source_repo.repo_name)) | |
674 |
|
674 | |||
675 | default_target_repo = source_repo |
|
675 | default_target_repo = source_repo | |
676 |
|
676 | |||
677 | if source_repo.parent: |
|
677 | if source_repo.parent: | |
678 | parent_vcs_obj = source_repo.parent.scm_instance() |
|
678 | parent_vcs_obj = source_repo.parent.scm_instance() | |
679 | if parent_vcs_obj and not parent_vcs_obj.is_empty(): |
|
679 | if parent_vcs_obj and not parent_vcs_obj.is_empty(): | |
680 | # change default if we have a parent repo |
|
680 | # change default if we have a parent repo | |
681 | default_target_repo = source_repo.parent |
|
681 | default_target_repo = source_repo.parent | |
682 |
|
682 | |||
683 | target_repo_data = PullRequestModel().generate_repo_data( |
|
683 | target_repo_data = PullRequestModel().generate_repo_data( | |
684 | default_target_repo, translator=self.request.translate) |
|
684 | default_target_repo, translator=self.request.translate) | |
685 |
|
685 | |||
686 | selected_source_ref = source_repo_data['refs']['selected_ref'] |
|
686 | selected_source_ref = source_repo_data['refs']['selected_ref'] | |
687 | title_source_ref = '' |
|
687 | title_source_ref = '' | |
688 | if selected_source_ref: |
|
688 | if selected_source_ref: | |
689 | title_source_ref = selected_source_ref.split(':', 2)[1] |
|
689 | title_source_ref = selected_source_ref.split(':', 2)[1] | |
690 | c.default_title = PullRequestModel().generate_pullrequest_title( |
|
690 | c.default_title = PullRequestModel().generate_pullrequest_title( | |
691 | source=source_repo.repo_name, |
|
691 | source=source_repo.repo_name, | |
692 | source_ref=title_source_ref, |
|
692 | source_ref=title_source_ref, | |
693 | target=default_target_repo.repo_name |
|
693 | target=default_target_repo.repo_name | |
694 | ) |
|
694 | ) | |
695 |
|
695 | |||
696 | c.default_repo_data = { |
|
696 | c.default_repo_data = { | |
697 | 'source_repo_name': source_repo.repo_name, |
|
697 | 'source_repo_name': source_repo.repo_name, | |
698 | 'source_refs_json': json.dumps(source_repo_data), |
|
698 | 'source_refs_json': json.dumps(source_repo_data), | |
699 | 'target_repo_name': default_target_repo.repo_name, |
|
699 | 'target_repo_name': default_target_repo.repo_name, | |
700 | 'target_refs_json': json.dumps(target_repo_data), |
|
700 | 'target_refs_json': json.dumps(target_repo_data), | |
701 | } |
|
701 | } | |
702 | c.default_source_ref = selected_source_ref |
|
702 | c.default_source_ref = selected_source_ref | |
703 |
|
703 | |||
704 | return self._get_template_context(c) |
|
704 | return self._get_template_context(c) | |
705 |
|
705 | |||
706 | @LoginRequired() |
|
706 | @LoginRequired() | |
707 | @NotAnonymous() |
|
707 | @NotAnonymous() | |
708 | @HasRepoPermissionAnyDecorator( |
|
708 | @HasRepoPermissionAnyDecorator( | |
709 | 'repository.read', 'repository.write', 'repository.admin') |
|
709 | 'repository.read', 'repository.write', 'repository.admin') | |
710 | @view_config( |
|
710 | @view_config( | |
711 | route_name='pullrequest_repo_refs', request_method='GET', |
|
711 | route_name='pullrequest_repo_refs', request_method='GET', | |
712 | renderer='json_ext', xhr=True) |
|
712 | renderer='json_ext', xhr=True) | |
713 | def pull_request_repo_refs(self): |
|
713 | def pull_request_repo_refs(self): | |
714 | self.load_default_context() |
|
714 | self.load_default_context() | |
715 | target_repo_name = self.request.matchdict['target_repo_name'] |
|
715 | target_repo_name = self.request.matchdict['target_repo_name'] | |
716 | repo = Repository.get_by_repo_name(target_repo_name) |
|
716 | repo = Repository.get_by_repo_name(target_repo_name) | |
717 | if not repo: |
|
717 | if not repo: | |
718 | raise HTTPNotFound() |
|
718 | raise HTTPNotFound() | |
719 |
|
719 | |||
720 | target_perm = HasRepoPermissionAny( |
|
720 | target_perm = HasRepoPermissionAny( | |
721 | 'repository.read', 'repository.write', 'repository.admin')( |
|
721 | 'repository.read', 'repository.write', 'repository.admin')( | |
722 | target_repo_name) |
|
722 | target_repo_name) | |
723 | if not target_perm: |
|
723 | if not target_perm: | |
724 | raise HTTPNotFound() |
|
724 | raise HTTPNotFound() | |
725 |
|
725 | |||
726 | return PullRequestModel().generate_repo_data( |
|
726 | return PullRequestModel().generate_repo_data( | |
727 | repo, translator=self.request.translate) |
|
727 | repo, translator=self.request.translate) | |
728 |
|
728 | |||
729 | @LoginRequired() |
|
729 | @LoginRequired() | |
730 | @NotAnonymous() |
|
730 | @NotAnonymous() | |
731 | @HasRepoPermissionAnyDecorator( |
|
731 | @HasRepoPermissionAnyDecorator( | |
732 | 'repository.read', 'repository.write', 'repository.admin') |
|
732 | 'repository.read', 'repository.write', 'repository.admin') | |
733 | @view_config( |
|
733 | @view_config( | |
734 | route_name='pullrequest_repo_destinations', request_method='GET', |
|
734 | route_name='pullrequest_repo_destinations', request_method='GET', | |
735 | renderer='json_ext', xhr=True) |
|
735 | renderer='json_ext', xhr=True) | |
736 | def pull_request_repo_destinations(self): |
|
736 | def pull_request_repo_destinations(self): | |
737 | _ = self.request.translate |
|
737 | _ = self.request.translate | |
738 | filter_query = self.request.GET.get('query') |
|
738 | filter_query = self.request.GET.get('query') | |
739 |
|
739 | |||
740 | query = Repository.query() \ |
|
740 | query = Repository.query() \ | |
741 | .order_by(func.length(Repository.repo_name)) \ |
|
741 | .order_by(func.length(Repository.repo_name)) \ | |
742 | .filter( |
|
742 | .filter( | |
743 | or_(Repository.repo_name == self.db_repo.repo_name, |
|
743 | or_(Repository.repo_name == self.db_repo.repo_name, | |
744 | Repository.fork_id == self.db_repo.repo_id)) |
|
744 | Repository.fork_id == self.db_repo.repo_id)) | |
745 |
|
745 | |||
746 | if filter_query: |
|
746 | if filter_query: | |
747 | ilike_expression = u'%{}%'.format(safe_unicode(filter_query)) |
|
747 | ilike_expression = u'%{}%'.format(safe_unicode(filter_query)) | |
748 | query = query.filter( |
|
748 | query = query.filter( | |
749 | Repository.repo_name.ilike(ilike_expression)) |
|
749 | Repository.repo_name.ilike(ilike_expression)) | |
750 |
|
750 | |||
751 | add_parent = False |
|
751 | add_parent = False | |
752 | if self.db_repo.parent: |
|
752 | if self.db_repo.parent: | |
753 | if filter_query in self.db_repo.parent.repo_name: |
|
753 | if filter_query in self.db_repo.parent.repo_name: | |
754 | parent_vcs_obj = self.db_repo.parent.scm_instance() |
|
754 | parent_vcs_obj = self.db_repo.parent.scm_instance() | |
755 | if parent_vcs_obj and not parent_vcs_obj.is_empty(): |
|
755 | if parent_vcs_obj and not parent_vcs_obj.is_empty(): | |
756 | add_parent = True |
|
756 | add_parent = True | |
757 |
|
757 | |||
758 | limit = 20 - 1 if add_parent else 20 |
|
758 | limit = 20 - 1 if add_parent else 20 | |
759 | all_repos = query.limit(limit).all() |
|
759 | all_repos = query.limit(limit).all() | |
760 | if add_parent: |
|
760 | if add_parent: | |
761 | all_repos += [self.db_repo.parent] |
|
761 | all_repos += [self.db_repo.parent] | |
762 |
|
762 | |||
763 | repos = [] |
|
763 | repos = [] | |
764 | for obj in ScmModel().get_repos(all_repos): |
|
764 | for obj in ScmModel().get_repos(all_repos): | |
765 | repos.append({ |
|
765 | repos.append({ | |
766 | 'id': obj['name'], |
|
766 | 'id': obj['name'], | |
767 | 'text': obj['name'], |
|
767 | 'text': obj['name'], | |
768 | 'type': 'repo', |
|
768 | 'type': 'repo', | |
769 |
' |
|
769 | 'repo_id': obj['dbrepo']['repo_id'], | |
|
770 | 'repo_type': obj['dbrepo']['repo_type'], | |||
|
771 | 'private': obj['dbrepo']['private'], | |||
|
772 | ||||
770 | }) |
|
773 | }) | |
771 |
|
774 | |||
772 | data = { |
|
775 | data = { | |
773 | 'more': False, |
|
776 | 'more': False, | |
774 | 'results': [{ |
|
777 | 'results': [{ | |
775 | 'text': _('Repositories'), |
|
778 | 'text': _('Repositories'), | |
776 | 'children': repos |
|
779 | 'children': repos | |
777 | }] if repos else [] |
|
780 | }] if repos else [] | |
778 | } |
|
781 | } | |
779 | return data |
|
782 | return data | |
780 |
|
783 | |||
781 | @LoginRequired() |
|
784 | @LoginRequired() | |
782 | @NotAnonymous() |
|
785 | @NotAnonymous() | |
783 | @HasRepoPermissionAnyDecorator( |
|
786 | @HasRepoPermissionAnyDecorator( | |
784 | 'repository.read', 'repository.write', 'repository.admin') |
|
787 | 'repository.read', 'repository.write', 'repository.admin') | |
785 | @CSRFRequired() |
|
788 | @CSRFRequired() | |
786 | @view_config( |
|
789 | @view_config( | |
787 | route_name='pullrequest_create', request_method='POST', |
|
790 | route_name='pullrequest_create', request_method='POST', | |
788 | renderer=None) |
|
791 | renderer=None) | |
789 | def pull_request_create(self): |
|
792 | def pull_request_create(self): | |
790 | _ = self.request.translate |
|
793 | _ = self.request.translate | |
791 | self.assure_not_empty_repo() |
|
794 | self.assure_not_empty_repo() | |
792 | self.load_default_context() |
|
795 | self.load_default_context() | |
793 |
|
796 | |||
794 | controls = peppercorn.parse(self.request.POST.items()) |
|
797 | controls = peppercorn.parse(self.request.POST.items()) | |
795 |
|
798 | |||
796 | try: |
|
799 | try: | |
797 | form = PullRequestForm( |
|
800 | form = PullRequestForm( | |
798 | self.request.translate, self.db_repo.repo_id)() |
|
801 | self.request.translate, self.db_repo.repo_id)() | |
799 | _form = form.to_python(controls) |
|
802 | _form = form.to_python(controls) | |
800 | except formencode.Invalid as errors: |
|
803 | except formencode.Invalid as errors: | |
801 | if errors.error_dict.get('revisions'): |
|
804 | if errors.error_dict.get('revisions'): | |
802 | msg = 'Revisions: %s' % errors.error_dict['revisions'] |
|
805 | msg = 'Revisions: %s' % errors.error_dict['revisions'] | |
803 | elif errors.error_dict.get('pullrequest_title'): |
|
806 | elif errors.error_dict.get('pullrequest_title'): | |
804 | msg = errors.error_dict.get('pullrequest_title') |
|
807 | msg = errors.error_dict.get('pullrequest_title') | |
805 | else: |
|
808 | else: | |
806 | msg = _('Error creating pull request: {}').format(errors) |
|
809 | msg = _('Error creating pull request: {}').format(errors) | |
807 | log.exception(msg) |
|
810 | log.exception(msg) | |
808 | h.flash(msg, 'error') |
|
811 | h.flash(msg, 'error') | |
809 |
|
812 | |||
810 | # would rather just go back to form ... |
|
813 | # would rather just go back to form ... | |
811 | raise HTTPFound( |
|
814 | raise HTTPFound( | |
812 | h.route_path('pullrequest_new', repo_name=self.db_repo_name)) |
|
815 | h.route_path('pullrequest_new', repo_name=self.db_repo_name)) | |
813 |
|
816 | |||
814 | source_repo = _form['source_repo'] |
|
817 | source_repo = _form['source_repo'] | |
815 | source_ref = _form['source_ref'] |
|
818 | source_ref = _form['source_ref'] | |
816 | target_repo = _form['target_repo'] |
|
819 | target_repo = _form['target_repo'] | |
817 | target_ref = _form['target_ref'] |
|
820 | target_ref = _form['target_ref'] | |
818 | commit_ids = _form['revisions'][::-1] |
|
821 | commit_ids = _form['revisions'][::-1] | |
819 |
|
822 | |||
820 | # find the ancestor for this pr |
|
823 | # find the ancestor for this pr | |
821 | source_db_repo = Repository.get_by_repo_name(_form['source_repo']) |
|
824 | source_db_repo = Repository.get_by_repo_name(_form['source_repo']) | |
822 | target_db_repo = Repository.get_by_repo_name(_form['target_repo']) |
|
825 | target_db_repo = Repository.get_by_repo_name(_form['target_repo']) | |
823 |
|
826 | |||
824 | # re-check permissions again here |
|
827 | # re-check permissions again here | |
825 | # source_repo we must have read permissions |
|
828 | # source_repo we must have read permissions | |
826 |
|
829 | |||
827 | source_perm = HasRepoPermissionAny( |
|
830 | source_perm = HasRepoPermissionAny( | |
828 | 'repository.read', |
|
831 | 'repository.read', | |
829 | 'repository.write', 'repository.admin')(source_db_repo.repo_name) |
|
832 | 'repository.write', 'repository.admin')(source_db_repo.repo_name) | |
830 | if not source_perm: |
|
833 | if not source_perm: | |
831 | msg = _('Not Enough permissions to source repo `{}`.'.format( |
|
834 | msg = _('Not Enough permissions to source repo `{}`.'.format( | |
832 | source_db_repo.repo_name)) |
|
835 | source_db_repo.repo_name)) | |
833 | h.flash(msg, category='error') |
|
836 | h.flash(msg, category='error') | |
834 | # copy the args back to redirect |
|
837 | # copy the args back to redirect | |
835 | org_query = self.request.GET.mixed() |
|
838 | org_query = self.request.GET.mixed() | |
836 | raise HTTPFound( |
|
839 | raise HTTPFound( | |
837 | h.route_path('pullrequest_new', repo_name=self.db_repo_name, |
|
840 | h.route_path('pullrequest_new', repo_name=self.db_repo_name, | |
838 | _query=org_query)) |
|
841 | _query=org_query)) | |
839 |
|
842 | |||
840 | # target repo we must have read permissions, and also later on |
|
843 | # target repo we must have read permissions, and also later on | |
841 | # we want to check branch permissions here |
|
844 | # we want to check branch permissions here | |
842 | target_perm = HasRepoPermissionAny( |
|
845 | target_perm = HasRepoPermissionAny( | |
843 | 'repository.read', |
|
846 | 'repository.read', | |
844 | 'repository.write', 'repository.admin')(target_db_repo.repo_name) |
|
847 | 'repository.write', 'repository.admin')(target_db_repo.repo_name) | |
845 | if not target_perm: |
|
848 | if not target_perm: | |
846 | msg = _('Not Enough permissions to target repo `{}`.'.format( |
|
849 | msg = _('Not Enough permissions to target repo `{}`.'.format( | |
847 | target_db_repo.repo_name)) |
|
850 | target_db_repo.repo_name)) | |
848 | h.flash(msg, category='error') |
|
851 | h.flash(msg, category='error') | |
849 | # copy the args back to redirect |
|
852 | # copy the args back to redirect | |
850 | org_query = self.request.GET.mixed() |
|
853 | org_query = self.request.GET.mixed() | |
851 | raise HTTPFound( |
|
854 | raise HTTPFound( | |
852 | h.route_path('pullrequest_new', repo_name=self.db_repo_name, |
|
855 | h.route_path('pullrequest_new', repo_name=self.db_repo_name, | |
853 | _query=org_query)) |
|
856 | _query=org_query)) | |
854 |
|
857 | |||
855 | source_scm = source_db_repo.scm_instance() |
|
858 | source_scm = source_db_repo.scm_instance() | |
856 | target_scm = target_db_repo.scm_instance() |
|
859 | target_scm = target_db_repo.scm_instance() | |
857 |
|
860 | |||
858 | source_commit = source_scm.get_commit(source_ref.split(':')[-1]) |
|
861 | source_commit = source_scm.get_commit(source_ref.split(':')[-1]) | |
859 | target_commit = target_scm.get_commit(target_ref.split(':')[-1]) |
|
862 | target_commit = target_scm.get_commit(target_ref.split(':')[-1]) | |
860 |
|
863 | |||
861 | ancestor = source_scm.get_common_ancestor( |
|
864 | ancestor = source_scm.get_common_ancestor( | |
862 | source_commit.raw_id, target_commit.raw_id, target_scm) |
|
865 | source_commit.raw_id, target_commit.raw_id, target_scm) | |
863 |
|
866 | |||
864 | target_ref_type, target_ref_name, __ = _form['target_ref'].split(':') |
|
867 | target_ref_type, target_ref_name, __ = _form['target_ref'].split(':') | |
865 | target_ref = ':'.join((target_ref_type, target_ref_name, ancestor)) |
|
868 | target_ref = ':'.join((target_ref_type, target_ref_name, ancestor)) | |
866 |
|
869 | |||
867 | pullrequest_title = _form['pullrequest_title'] |
|
870 | pullrequest_title = _form['pullrequest_title'] | |
868 | title_source_ref = source_ref.split(':', 2)[1] |
|
871 | title_source_ref = source_ref.split(':', 2)[1] | |
869 | if not pullrequest_title: |
|
872 | if not pullrequest_title: | |
870 | pullrequest_title = PullRequestModel().generate_pullrequest_title( |
|
873 | pullrequest_title = PullRequestModel().generate_pullrequest_title( | |
871 | source=source_repo, |
|
874 | source=source_repo, | |
872 | source_ref=title_source_ref, |
|
875 | source_ref=title_source_ref, | |
873 | target=target_repo |
|
876 | target=target_repo | |
874 | ) |
|
877 | ) | |
875 |
|
878 | |||
876 | description = _form['pullrequest_desc'] |
|
879 | description = _form['pullrequest_desc'] | |
877 |
|
880 | |||
878 | get_default_reviewers_data, validate_default_reviewers = \ |
|
881 | get_default_reviewers_data, validate_default_reviewers = \ | |
879 | PullRequestModel().get_reviewer_functions() |
|
882 | PullRequestModel().get_reviewer_functions() | |
880 |
|
883 | |||
881 | # recalculate reviewers logic, to make sure we can validate this |
|
884 | # recalculate reviewers logic, to make sure we can validate this | |
882 | reviewer_rules = get_default_reviewers_data( |
|
885 | reviewer_rules = get_default_reviewers_data( | |
883 | self._rhodecode_db_user, source_db_repo, |
|
886 | self._rhodecode_db_user, source_db_repo, | |
884 | source_commit, target_db_repo, target_commit) |
|
887 | source_commit, target_db_repo, target_commit) | |
885 |
|
888 | |||
886 | given_reviewers = _form['review_members'] |
|
889 | given_reviewers = _form['review_members'] | |
887 | reviewers = validate_default_reviewers(given_reviewers, reviewer_rules) |
|
890 | reviewers = validate_default_reviewers(given_reviewers, reviewer_rules) | |
888 |
|
891 | |||
889 | try: |
|
892 | try: | |
890 | pull_request = PullRequestModel().create( |
|
893 | pull_request = PullRequestModel().create( | |
891 | self._rhodecode_user.user_id, source_repo, source_ref, |
|
894 | self._rhodecode_user.user_id, source_repo, source_ref, | |
892 | target_repo, target_ref, commit_ids, reviewers, |
|
895 | target_repo, target_ref, commit_ids, reviewers, | |
893 | pullrequest_title, description, reviewer_rules |
|
896 | pullrequest_title, description, reviewer_rules | |
894 | ) |
|
897 | ) | |
895 | Session().commit() |
|
898 | Session().commit() | |
896 |
|
899 | |||
897 | h.flash(_('Successfully opened new pull request'), |
|
900 | h.flash(_('Successfully opened new pull request'), | |
898 | category='success') |
|
901 | category='success') | |
899 | except Exception: |
|
902 | except Exception: | |
900 | msg = _('Error occurred during creation of this pull request.') |
|
903 | msg = _('Error occurred during creation of this pull request.') | |
901 | log.exception(msg) |
|
904 | log.exception(msg) | |
902 | h.flash(msg, category='error') |
|
905 | h.flash(msg, category='error') | |
903 |
|
906 | |||
904 | # copy the args back to redirect |
|
907 | # copy the args back to redirect | |
905 | org_query = self.request.GET.mixed() |
|
908 | org_query = self.request.GET.mixed() | |
906 | raise HTTPFound( |
|
909 | raise HTTPFound( | |
907 | h.route_path('pullrequest_new', repo_name=self.db_repo_name, |
|
910 | h.route_path('pullrequest_new', repo_name=self.db_repo_name, | |
908 | _query=org_query)) |
|
911 | _query=org_query)) | |
909 |
|
912 | |||
910 | raise HTTPFound( |
|
913 | raise HTTPFound( | |
911 | h.route_path('pullrequest_show', repo_name=target_repo, |
|
914 | h.route_path('pullrequest_show', repo_name=target_repo, | |
912 | pull_request_id=pull_request.pull_request_id)) |
|
915 | pull_request_id=pull_request.pull_request_id)) | |
913 |
|
916 | |||
914 | @LoginRequired() |
|
917 | @LoginRequired() | |
915 | @NotAnonymous() |
|
918 | @NotAnonymous() | |
916 | @HasRepoPermissionAnyDecorator( |
|
919 | @HasRepoPermissionAnyDecorator( | |
917 | 'repository.read', 'repository.write', 'repository.admin') |
|
920 | 'repository.read', 'repository.write', 'repository.admin') | |
918 | @CSRFRequired() |
|
921 | @CSRFRequired() | |
919 | @view_config( |
|
922 | @view_config( | |
920 | route_name='pullrequest_update', request_method='POST', |
|
923 | route_name='pullrequest_update', request_method='POST', | |
921 | renderer='json_ext') |
|
924 | renderer='json_ext') | |
922 | def pull_request_update(self): |
|
925 | def pull_request_update(self): | |
923 | pull_request = PullRequest.get_or_404( |
|
926 | pull_request = PullRequest.get_or_404( | |
924 | self.request.matchdict['pull_request_id']) |
|
927 | self.request.matchdict['pull_request_id']) | |
925 | _ = self.request.translate |
|
928 | _ = self.request.translate | |
926 |
|
929 | |||
927 | self.load_default_context() |
|
930 | self.load_default_context() | |
928 |
|
931 | |||
929 | if pull_request.is_closed(): |
|
932 | if pull_request.is_closed(): | |
930 | log.debug('update: forbidden because pull request is closed') |
|
933 | log.debug('update: forbidden because pull request is closed') | |
931 | msg = _(u'Cannot update closed pull requests.') |
|
934 | msg = _(u'Cannot update closed pull requests.') | |
932 | h.flash(msg, category='error') |
|
935 | h.flash(msg, category='error') | |
933 | return True |
|
936 | return True | |
934 |
|
937 | |||
935 | # only owner or admin can update it |
|
938 | # only owner or admin can update it | |
936 | allowed_to_update = PullRequestModel().check_user_update( |
|
939 | allowed_to_update = PullRequestModel().check_user_update( | |
937 | pull_request, self._rhodecode_user) |
|
940 | pull_request, self._rhodecode_user) | |
938 | if allowed_to_update: |
|
941 | if allowed_to_update: | |
939 | controls = peppercorn.parse(self.request.POST.items()) |
|
942 | controls = peppercorn.parse(self.request.POST.items()) | |
940 |
|
943 | |||
941 | if 'review_members' in controls: |
|
944 | if 'review_members' in controls: | |
942 | self._update_reviewers( |
|
945 | self._update_reviewers( | |
943 | pull_request, controls['review_members'], |
|
946 | pull_request, controls['review_members'], | |
944 | pull_request.reviewer_data) |
|
947 | pull_request.reviewer_data) | |
945 | elif str2bool(self.request.POST.get('update_commits', 'false')): |
|
948 | elif str2bool(self.request.POST.get('update_commits', 'false')): | |
946 | self._update_commits(pull_request) |
|
949 | self._update_commits(pull_request) | |
947 | elif str2bool(self.request.POST.get('edit_pull_request', 'false')): |
|
950 | elif str2bool(self.request.POST.get('edit_pull_request', 'false')): | |
948 | self._edit_pull_request(pull_request) |
|
951 | self._edit_pull_request(pull_request) | |
949 | else: |
|
952 | else: | |
950 | raise HTTPBadRequest() |
|
953 | raise HTTPBadRequest() | |
951 | return True |
|
954 | return True | |
952 | raise HTTPForbidden() |
|
955 | raise HTTPForbidden() | |
953 |
|
956 | |||
954 | def _edit_pull_request(self, pull_request): |
|
957 | def _edit_pull_request(self, pull_request): | |
955 | _ = self.request.translate |
|
958 | _ = self.request.translate | |
956 | try: |
|
959 | try: | |
957 | PullRequestModel().edit( |
|
960 | PullRequestModel().edit( | |
958 | pull_request, self.request.POST.get('title'), |
|
961 | pull_request, self.request.POST.get('title'), | |
959 | self.request.POST.get('description'), self._rhodecode_user) |
|
962 | self.request.POST.get('description'), self._rhodecode_user) | |
960 | except ValueError: |
|
963 | except ValueError: | |
961 | msg = _(u'Cannot update closed pull requests.') |
|
964 | msg = _(u'Cannot update closed pull requests.') | |
962 | h.flash(msg, category='error') |
|
965 | h.flash(msg, category='error') | |
963 | return |
|
966 | return | |
964 | else: |
|
967 | else: | |
965 | Session().commit() |
|
968 | Session().commit() | |
966 |
|
969 | |||
967 | msg = _(u'Pull request title & description updated.') |
|
970 | msg = _(u'Pull request title & description updated.') | |
968 | h.flash(msg, category='success') |
|
971 | h.flash(msg, category='success') | |
969 | return |
|
972 | return | |
970 |
|
973 | |||
971 | def _update_commits(self, pull_request): |
|
974 | def _update_commits(self, pull_request): | |
972 | _ = self.request.translate |
|
975 | _ = self.request.translate | |
973 | resp = PullRequestModel().update_commits(pull_request) |
|
976 | resp = PullRequestModel().update_commits(pull_request) | |
974 |
|
977 | |||
975 | if resp.executed: |
|
978 | if resp.executed: | |
976 |
|
979 | |||
977 | if resp.target_changed and resp.source_changed: |
|
980 | if resp.target_changed and resp.source_changed: | |
978 | changed = 'target and source repositories' |
|
981 | changed = 'target and source repositories' | |
979 | elif resp.target_changed and not resp.source_changed: |
|
982 | elif resp.target_changed and not resp.source_changed: | |
980 | changed = 'target repository' |
|
983 | changed = 'target repository' | |
981 | elif not resp.target_changed and resp.source_changed: |
|
984 | elif not resp.target_changed and resp.source_changed: | |
982 | changed = 'source repository' |
|
985 | changed = 'source repository' | |
983 | else: |
|
986 | else: | |
984 | changed = 'nothing' |
|
987 | changed = 'nothing' | |
985 |
|
988 | |||
986 | msg = _( |
|
989 | msg = _( | |
987 | u'Pull request updated to "{source_commit_id}" with ' |
|
990 | u'Pull request updated to "{source_commit_id}" with ' | |
988 | u'{count_added} added, {count_removed} removed commits. ' |
|
991 | u'{count_added} added, {count_removed} removed commits. ' | |
989 | u'Source of changes: {change_source}') |
|
992 | u'Source of changes: {change_source}') | |
990 | msg = msg.format( |
|
993 | msg = msg.format( | |
991 | source_commit_id=pull_request.source_ref_parts.commit_id, |
|
994 | source_commit_id=pull_request.source_ref_parts.commit_id, | |
992 | count_added=len(resp.changes.added), |
|
995 | count_added=len(resp.changes.added), | |
993 | count_removed=len(resp.changes.removed), |
|
996 | count_removed=len(resp.changes.removed), | |
994 | change_source=changed) |
|
997 | change_source=changed) | |
995 | h.flash(msg, category='success') |
|
998 | h.flash(msg, category='success') | |
996 |
|
999 | |||
997 | channel = '/repo${}$/pr/{}'.format( |
|
1000 | channel = '/repo${}$/pr/{}'.format( | |
998 | pull_request.target_repo.repo_name, |
|
1001 | pull_request.target_repo.repo_name, | |
999 | pull_request.pull_request_id) |
|
1002 | pull_request.pull_request_id) | |
1000 | message = msg + ( |
|
1003 | message = msg + ( | |
1001 | ' - <a onclick="window.location.reload()">' |
|
1004 | ' - <a onclick="window.location.reload()">' | |
1002 | '<strong>{}</strong></a>'.format(_('Reload page'))) |
|
1005 | '<strong>{}</strong></a>'.format(_('Reload page'))) | |
1003 | channelstream.post_message( |
|
1006 | channelstream.post_message( | |
1004 | channel, message, self._rhodecode_user.username, |
|
1007 | channel, message, self._rhodecode_user.username, | |
1005 | registry=self.request.registry) |
|
1008 | registry=self.request.registry) | |
1006 | else: |
|
1009 | else: | |
1007 | msg = PullRequestModel.UPDATE_STATUS_MESSAGES[resp.reason] |
|
1010 | msg = PullRequestModel.UPDATE_STATUS_MESSAGES[resp.reason] | |
1008 | warning_reasons = [ |
|
1011 | warning_reasons = [ | |
1009 | UpdateFailureReason.NO_CHANGE, |
|
1012 | UpdateFailureReason.NO_CHANGE, | |
1010 | UpdateFailureReason.WRONG_REF_TYPE, |
|
1013 | UpdateFailureReason.WRONG_REF_TYPE, | |
1011 | ] |
|
1014 | ] | |
1012 | category = 'warning' if resp.reason in warning_reasons else 'error' |
|
1015 | category = 'warning' if resp.reason in warning_reasons else 'error' | |
1013 | h.flash(msg, category=category) |
|
1016 | h.flash(msg, category=category) | |
1014 |
|
1017 | |||
1015 | @LoginRequired() |
|
1018 | @LoginRequired() | |
1016 | @NotAnonymous() |
|
1019 | @NotAnonymous() | |
1017 | @HasRepoPermissionAnyDecorator( |
|
1020 | @HasRepoPermissionAnyDecorator( | |
1018 | 'repository.read', 'repository.write', 'repository.admin') |
|
1021 | 'repository.read', 'repository.write', 'repository.admin') | |
1019 | @CSRFRequired() |
|
1022 | @CSRFRequired() | |
1020 | @view_config( |
|
1023 | @view_config( | |
1021 | route_name='pullrequest_merge', request_method='POST', |
|
1024 | route_name='pullrequest_merge', request_method='POST', | |
1022 | renderer='json_ext') |
|
1025 | renderer='json_ext') | |
1023 | def pull_request_merge(self): |
|
1026 | def pull_request_merge(self): | |
1024 | """ |
|
1027 | """ | |
1025 | Merge will perform a server-side merge of the specified |
|
1028 | Merge will perform a server-side merge of the specified | |
1026 | pull request, if the pull request is approved and mergeable. |
|
1029 | pull request, if the pull request is approved and mergeable. | |
1027 | After successful merging, the pull request is automatically |
|
1030 | After successful merging, the pull request is automatically | |
1028 | closed, with a relevant comment. |
|
1031 | closed, with a relevant comment. | |
1029 | """ |
|
1032 | """ | |
1030 | pull_request = PullRequest.get_or_404( |
|
1033 | pull_request = PullRequest.get_or_404( | |
1031 | self.request.matchdict['pull_request_id']) |
|
1034 | self.request.matchdict['pull_request_id']) | |
1032 |
|
1035 | |||
1033 | self.load_default_context() |
|
1036 | self.load_default_context() | |
1034 | check = MergeCheck.validate(pull_request, self._rhodecode_db_user, |
|
1037 | check = MergeCheck.validate(pull_request, self._rhodecode_db_user, | |
1035 | translator=self.request.translate) |
|
1038 | translator=self.request.translate) | |
1036 | merge_possible = not check.failed |
|
1039 | merge_possible = not check.failed | |
1037 |
|
1040 | |||
1038 | for err_type, error_msg in check.errors: |
|
1041 | for err_type, error_msg in check.errors: | |
1039 | h.flash(error_msg, category=err_type) |
|
1042 | h.flash(error_msg, category=err_type) | |
1040 |
|
1043 | |||
1041 | if merge_possible: |
|
1044 | if merge_possible: | |
1042 | log.debug("Pre-conditions checked, trying to merge.") |
|
1045 | log.debug("Pre-conditions checked, trying to merge.") | |
1043 | extras = vcs_operation_context( |
|
1046 | extras = vcs_operation_context( | |
1044 | self.request.environ, repo_name=pull_request.target_repo.repo_name, |
|
1047 | self.request.environ, repo_name=pull_request.target_repo.repo_name, | |
1045 | username=self._rhodecode_db_user.username, action='push', |
|
1048 | username=self._rhodecode_db_user.username, action='push', | |
1046 | scm=pull_request.target_repo.repo_type) |
|
1049 | scm=pull_request.target_repo.repo_type) | |
1047 | self._merge_pull_request( |
|
1050 | self._merge_pull_request( | |
1048 | pull_request, self._rhodecode_db_user, extras) |
|
1051 | pull_request, self._rhodecode_db_user, extras) | |
1049 | else: |
|
1052 | else: | |
1050 | log.debug("Pre-conditions failed, NOT merging.") |
|
1053 | log.debug("Pre-conditions failed, NOT merging.") | |
1051 |
|
1054 | |||
1052 | raise HTTPFound( |
|
1055 | raise HTTPFound( | |
1053 | h.route_path('pullrequest_show', |
|
1056 | h.route_path('pullrequest_show', | |
1054 | repo_name=pull_request.target_repo.repo_name, |
|
1057 | repo_name=pull_request.target_repo.repo_name, | |
1055 | pull_request_id=pull_request.pull_request_id)) |
|
1058 | pull_request_id=pull_request.pull_request_id)) | |
1056 |
|
1059 | |||
1057 | def _merge_pull_request(self, pull_request, user, extras): |
|
1060 | def _merge_pull_request(self, pull_request, user, extras): | |
1058 | _ = self.request.translate |
|
1061 | _ = self.request.translate | |
1059 | merge_resp = PullRequestModel().merge(pull_request, user, extras=extras) |
|
1062 | merge_resp = PullRequestModel().merge(pull_request, user, extras=extras) | |
1060 |
|
1063 | |||
1061 | if merge_resp.executed: |
|
1064 | if merge_resp.executed: | |
1062 | log.debug("The merge was successful, closing the pull request.") |
|
1065 | log.debug("The merge was successful, closing the pull request.") | |
1063 | PullRequestModel().close_pull_request( |
|
1066 | PullRequestModel().close_pull_request( | |
1064 | pull_request.pull_request_id, user) |
|
1067 | pull_request.pull_request_id, user) | |
1065 | Session().commit() |
|
1068 | Session().commit() | |
1066 | msg = _('Pull request was successfully merged and closed.') |
|
1069 | msg = _('Pull request was successfully merged and closed.') | |
1067 | h.flash(msg, category='success') |
|
1070 | h.flash(msg, category='success') | |
1068 | else: |
|
1071 | else: | |
1069 | log.debug( |
|
1072 | log.debug( | |
1070 | "The merge was not successful. Merge response: %s", |
|
1073 | "The merge was not successful. Merge response: %s", | |
1071 | merge_resp) |
|
1074 | merge_resp) | |
1072 | msg = PullRequestModel().merge_status_message( |
|
1075 | msg = PullRequestModel().merge_status_message( | |
1073 | merge_resp.failure_reason) |
|
1076 | merge_resp.failure_reason) | |
1074 | h.flash(msg, category='error') |
|
1077 | h.flash(msg, category='error') | |
1075 |
|
1078 | |||
1076 | def _update_reviewers(self, pull_request, review_members, reviewer_rules): |
|
1079 | def _update_reviewers(self, pull_request, review_members, reviewer_rules): | |
1077 | _ = self.request.translate |
|
1080 | _ = self.request.translate | |
1078 | get_default_reviewers_data, validate_default_reviewers = \ |
|
1081 | get_default_reviewers_data, validate_default_reviewers = \ | |
1079 | PullRequestModel().get_reviewer_functions() |
|
1082 | PullRequestModel().get_reviewer_functions() | |
1080 |
|
1083 | |||
1081 | try: |
|
1084 | try: | |
1082 | reviewers = validate_default_reviewers(review_members, reviewer_rules) |
|
1085 | reviewers = validate_default_reviewers(review_members, reviewer_rules) | |
1083 | except ValueError as e: |
|
1086 | except ValueError as e: | |
1084 | log.error('Reviewers Validation: {}'.format(e)) |
|
1087 | log.error('Reviewers Validation: {}'.format(e)) | |
1085 | h.flash(e, category='error') |
|
1088 | h.flash(e, category='error') | |
1086 | return |
|
1089 | return | |
1087 |
|
1090 | |||
1088 | PullRequestModel().update_reviewers( |
|
1091 | PullRequestModel().update_reviewers( | |
1089 | pull_request, reviewers, self._rhodecode_user) |
|
1092 | pull_request, reviewers, self._rhodecode_user) | |
1090 | h.flash(_('Pull request reviewers updated.'), category='success') |
|
1093 | h.flash(_('Pull request reviewers updated.'), category='success') | |
1091 | Session().commit() |
|
1094 | Session().commit() | |
1092 |
|
1095 | |||
1093 | @LoginRequired() |
|
1096 | @LoginRequired() | |
1094 | @NotAnonymous() |
|
1097 | @NotAnonymous() | |
1095 | @HasRepoPermissionAnyDecorator( |
|
1098 | @HasRepoPermissionAnyDecorator( | |
1096 | 'repository.read', 'repository.write', 'repository.admin') |
|
1099 | 'repository.read', 'repository.write', 'repository.admin') | |
1097 | @CSRFRequired() |
|
1100 | @CSRFRequired() | |
1098 | @view_config( |
|
1101 | @view_config( | |
1099 | route_name='pullrequest_delete', request_method='POST', |
|
1102 | route_name='pullrequest_delete', request_method='POST', | |
1100 | renderer='json_ext') |
|
1103 | renderer='json_ext') | |
1101 | def pull_request_delete(self): |
|
1104 | def pull_request_delete(self): | |
1102 | _ = self.request.translate |
|
1105 | _ = self.request.translate | |
1103 |
|
1106 | |||
1104 | pull_request = PullRequest.get_or_404( |
|
1107 | pull_request = PullRequest.get_or_404( | |
1105 | self.request.matchdict['pull_request_id']) |
|
1108 | self.request.matchdict['pull_request_id']) | |
1106 | self.load_default_context() |
|
1109 | self.load_default_context() | |
1107 |
|
1110 | |||
1108 | pr_closed = pull_request.is_closed() |
|
1111 | pr_closed = pull_request.is_closed() | |
1109 | allowed_to_delete = PullRequestModel().check_user_delete( |
|
1112 | allowed_to_delete = PullRequestModel().check_user_delete( | |
1110 | pull_request, self._rhodecode_user) and not pr_closed |
|
1113 | pull_request, self._rhodecode_user) and not pr_closed | |
1111 |
|
1114 | |||
1112 | # only owner can delete it ! |
|
1115 | # only owner can delete it ! | |
1113 | if allowed_to_delete: |
|
1116 | if allowed_to_delete: | |
1114 | PullRequestModel().delete(pull_request, self._rhodecode_user) |
|
1117 | PullRequestModel().delete(pull_request, self._rhodecode_user) | |
1115 | Session().commit() |
|
1118 | Session().commit() | |
1116 | h.flash(_('Successfully deleted pull request'), |
|
1119 | h.flash(_('Successfully deleted pull request'), | |
1117 | category='success') |
|
1120 | category='success') | |
1118 | raise HTTPFound(h.route_path('pullrequest_show_all', |
|
1121 | raise HTTPFound(h.route_path('pullrequest_show_all', | |
1119 | repo_name=self.db_repo_name)) |
|
1122 | repo_name=self.db_repo_name)) | |
1120 |
|
1123 | |||
1121 | log.warning('user %s tried to delete pull request without access', |
|
1124 | log.warning('user %s tried to delete pull request without access', | |
1122 | self._rhodecode_user) |
|
1125 | self._rhodecode_user) | |
1123 | raise HTTPNotFound() |
|
1126 | raise HTTPNotFound() | |
1124 |
|
1127 | |||
1125 | @LoginRequired() |
|
1128 | @LoginRequired() | |
1126 | @NotAnonymous() |
|
1129 | @NotAnonymous() | |
1127 | @HasRepoPermissionAnyDecorator( |
|
1130 | @HasRepoPermissionAnyDecorator( | |
1128 | 'repository.read', 'repository.write', 'repository.admin') |
|
1131 | 'repository.read', 'repository.write', 'repository.admin') | |
1129 | @CSRFRequired() |
|
1132 | @CSRFRequired() | |
1130 | @view_config( |
|
1133 | @view_config( | |
1131 | route_name='pullrequest_comment_create', request_method='POST', |
|
1134 | route_name='pullrequest_comment_create', request_method='POST', | |
1132 | renderer='json_ext') |
|
1135 | renderer='json_ext') | |
1133 | def pull_request_comment_create(self): |
|
1136 | def pull_request_comment_create(self): | |
1134 | _ = self.request.translate |
|
1137 | _ = self.request.translate | |
1135 |
|
1138 | |||
1136 | pull_request = PullRequest.get_or_404( |
|
1139 | pull_request = PullRequest.get_or_404( | |
1137 | self.request.matchdict['pull_request_id']) |
|
1140 | self.request.matchdict['pull_request_id']) | |
1138 | pull_request_id = pull_request.pull_request_id |
|
1141 | pull_request_id = pull_request.pull_request_id | |
1139 |
|
1142 | |||
1140 | if pull_request.is_closed(): |
|
1143 | if pull_request.is_closed(): | |
1141 | log.debug('comment: forbidden because pull request is closed') |
|
1144 | log.debug('comment: forbidden because pull request is closed') | |
1142 | raise HTTPForbidden() |
|
1145 | raise HTTPForbidden() | |
1143 |
|
1146 | |||
1144 | allowed_to_comment = PullRequestModel().check_user_comment( |
|
1147 | allowed_to_comment = PullRequestModel().check_user_comment( | |
1145 | pull_request, self._rhodecode_user) |
|
1148 | pull_request, self._rhodecode_user) | |
1146 | if not allowed_to_comment: |
|
1149 | if not allowed_to_comment: | |
1147 | log.debug( |
|
1150 | log.debug( | |
1148 | 'comment: forbidden because pull request is from forbidden repo') |
|
1151 | 'comment: forbidden because pull request is from forbidden repo') | |
1149 | raise HTTPForbidden() |
|
1152 | raise HTTPForbidden() | |
1150 |
|
1153 | |||
1151 | c = self.load_default_context() |
|
1154 | c = self.load_default_context() | |
1152 |
|
1155 | |||
1153 | status = self.request.POST.get('changeset_status', None) |
|
1156 | status = self.request.POST.get('changeset_status', None) | |
1154 | text = self.request.POST.get('text') |
|
1157 | text = self.request.POST.get('text') | |
1155 | comment_type = self.request.POST.get('comment_type') |
|
1158 | comment_type = self.request.POST.get('comment_type') | |
1156 | resolves_comment_id = self.request.POST.get('resolves_comment_id', None) |
|
1159 | resolves_comment_id = self.request.POST.get('resolves_comment_id', None) | |
1157 | close_pull_request = self.request.POST.get('close_pull_request') |
|
1160 | close_pull_request = self.request.POST.get('close_pull_request') | |
1158 |
|
1161 | |||
1159 | # the logic here should work like following, if we submit close |
|
1162 | # the logic here should work like following, if we submit close | |
1160 | # pr comment, use `close_pull_request_with_comment` function |
|
1163 | # pr comment, use `close_pull_request_with_comment` function | |
1161 | # else handle regular comment logic |
|
1164 | # else handle regular comment logic | |
1162 |
|
1165 | |||
1163 | if close_pull_request: |
|
1166 | if close_pull_request: | |
1164 | # only owner or admin or person with write permissions |
|
1167 | # only owner or admin or person with write permissions | |
1165 | allowed_to_close = PullRequestModel().check_user_update( |
|
1168 | allowed_to_close = PullRequestModel().check_user_update( | |
1166 | pull_request, self._rhodecode_user) |
|
1169 | pull_request, self._rhodecode_user) | |
1167 | if not allowed_to_close: |
|
1170 | if not allowed_to_close: | |
1168 | log.debug('comment: forbidden because not allowed to close ' |
|
1171 | log.debug('comment: forbidden because not allowed to close ' | |
1169 | 'pull request %s', pull_request_id) |
|
1172 | 'pull request %s', pull_request_id) | |
1170 | raise HTTPForbidden() |
|
1173 | raise HTTPForbidden() | |
1171 | comment, status = PullRequestModel().close_pull_request_with_comment( |
|
1174 | comment, status = PullRequestModel().close_pull_request_with_comment( | |
1172 | pull_request, self._rhodecode_user, self.db_repo, message=text) |
|
1175 | pull_request, self._rhodecode_user, self.db_repo, message=text) | |
1173 | Session().flush() |
|
1176 | Session().flush() | |
1174 | events.trigger( |
|
1177 | events.trigger( | |
1175 | events.PullRequestCommentEvent(pull_request, comment)) |
|
1178 | events.PullRequestCommentEvent(pull_request, comment)) | |
1176 |
|
1179 | |||
1177 | else: |
|
1180 | else: | |
1178 | # regular comment case, could be inline, or one with status. |
|
1181 | # regular comment case, could be inline, or one with status. | |
1179 | # for that one we check also permissions |
|
1182 | # for that one we check also permissions | |
1180 |
|
1183 | |||
1181 | allowed_to_change_status = PullRequestModel().check_user_change_status( |
|
1184 | allowed_to_change_status = PullRequestModel().check_user_change_status( | |
1182 | pull_request, self._rhodecode_user) |
|
1185 | pull_request, self._rhodecode_user) | |
1183 |
|
1186 | |||
1184 | if status and allowed_to_change_status: |
|
1187 | if status and allowed_to_change_status: | |
1185 | message = (_('Status change %(transition_icon)s %(status)s') |
|
1188 | message = (_('Status change %(transition_icon)s %(status)s') | |
1186 | % {'transition_icon': '>', |
|
1189 | % {'transition_icon': '>', | |
1187 | 'status': ChangesetStatus.get_status_lbl(status)}) |
|
1190 | 'status': ChangesetStatus.get_status_lbl(status)}) | |
1188 | text = text or message |
|
1191 | text = text or message | |
1189 |
|
1192 | |||
1190 | comment = CommentsModel().create( |
|
1193 | comment = CommentsModel().create( | |
1191 | text=text, |
|
1194 | text=text, | |
1192 | repo=self.db_repo.repo_id, |
|
1195 | repo=self.db_repo.repo_id, | |
1193 | user=self._rhodecode_user.user_id, |
|
1196 | user=self._rhodecode_user.user_id, | |
1194 | pull_request=pull_request, |
|
1197 | pull_request=pull_request, | |
1195 | f_path=self.request.POST.get('f_path'), |
|
1198 | f_path=self.request.POST.get('f_path'), | |
1196 | line_no=self.request.POST.get('line'), |
|
1199 | line_no=self.request.POST.get('line'), | |
1197 | status_change=(ChangesetStatus.get_status_lbl(status) |
|
1200 | status_change=(ChangesetStatus.get_status_lbl(status) | |
1198 | if status and allowed_to_change_status else None), |
|
1201 | if status and allowed_to_change_status else None), | |
1199 | status_change_type=(status |
|
1202 | status_change_type=(status | |
1200 | if status and allowed_to_change_status else None), |
|
1203 | if status and allowed_to_change_status else None), | |
1201 | comment_type=comment_type, |
|
1204 | comment_type=comment_type, | |
1202 | resolves_comment_id=resolves_comment_id |
|
1205 | resolves_comment_id=resolves_comment_id | |
1203 | ) |
|
1206 | ) | |
1204 |
|
1207 | |||
1205 | if allowed_to_change_status: |
|
1208 | if allowed_to_change_status: | |
1206 | # calculate old status before we change it |
|
1209 | # calculate old status before we change it | |
1207 | old_calculated_status = pull_request.calculated_review_status() |
|
1210 | old_calculated_status = pull_request.calculated_review_status() | |
1208 |
|
1211 | |||
1209 | # get status if set ! |
|
1212 | # get status if set ! | |
1210 | if status: |
|
1213 | if status: | |
1211 | ChangesetStatusModel().set_status( |
|
1214 | ChangesetStatusModel().set_status( | |
1212 | self.db_repo.repo_id, |
|
1215 | self.db_repo.repo_id, | |
1213 | status, |
|
1216 | status, | |
1214 | self._rhodecode_user.user_id, |
|
1217 | self._rhodecode_user.user_id, | |
1215 | comment, |
|
1218 | comment, | |
1216 | pull_request=pull_request |
|
1219 | pull_request=pull_request | |
1217 | ) |
|
1220 | ) | |
1218 |
|
1221 | |||
1219 | Session().flush() |
|
1222 | Session().flush() | |
1220 | # this is somehow required to get access to some relationship |
|
1223 | # this is somehow required to get access to some relationship | |
1221 | # loaded on comment |
|
1224 | # loaded on comment | |
1222 | Session().refresh(comment) |
|
1225 | Session().refresh(comment) | |
1223 |
|
1226 | |||
1224 | events.trigger( |
|
1227 | events.trigger( | |
1225 | events.PullRequestCommentEvent(pull_request, comment)) |
|
1228 | events.PullRequestCommentEvent(pull_request, comment)) | |
1226 |
|
1229 | |||
1227 | # we now calculate the status of pull request, and based on that |
|
1230 | # we now calculate the status of pull request, and based on that | |
1228 | # calculation we set the commits status |
|
1231 | # calculation we set the commits status | |
1229 | calculated_status = pull_request.calculated_review_status() |
|
1232 | calculated_status = pull_request.calculated_review_status() | |
1230 | if old_calculated_status != calculated_status: |
|
1233 | if old_calculated_status != calculated_status: | |
1231 | PullRequestModel()._trigger_pull_request_hook( |
|
1234 | PullRequestModel()._trigger_pull_request_hook( | |
1232 | pull_request, self._rhodecode_user, 'review_status_change') |
|
1235 | pull_request, self._rhodecode_user, 'review_status_change') | |
1233 |
|
1236 | |||
1234 | Session().commit() |
|
1237 | Session().commit() | |
1235 |
|
1238 | |||
1236 | data = { |
|
1239 | data = { | |
1237 | 'target_id': h.safeid(h.safe_unicode( |
|
1240 | 'target_id': h.safeid(h.safe_unicode( | |
1238 | self.request.POST.get('f_path'))), |
|
1241 | self.request.POST.get('f_path'))), | |
1239 | } |
|
1242 | } | |
1240 | if comment: |
|
1243 | if comment: | |
1241 | c.co = comment |
|
1244 | c.co = comment | |
1242 | rendered_comment = render( |
|
1245 | rendered_comment = render( | |
1243 | 'rhodecode:templates/changeset/changeset_comment_block.mako', |
|
1246 | 'rhodecode:templates/changeset/changeset_comment_block.mako', | |
1244 | self._get_template_context(c), self.request) |
|
1247 | self._get_template_context(c), self.request) | |
1245 |
|
1248 | |||
1246 | data.update(comment.get_dict()) |
|
1249 | data.update(comment.get_dict()) | |
1247 | data.update({'rendered_text': rendered_comment}) |
|
1250 | data.update({'rendered_text': rendered_comment}) | |
1248 |
|
1251 | |||
1249 | return data |
|
1252 | return data | |
1250 |
|
1253 | |||
1251 | @LoginRequired() |
|
1254 | @LoginRequired() | |
1252 | @NotAnonymous() |
|
1255 | @NotAnonymous() | |
1253 | @HasRepoPermissionAnyDecorator( |
|
1256 | @HasRepoPermissionAnyDecorator( | |
1254 | 'repository.read', 'repository.write', 'repository.admin') |
|
1257 | 'repository.read', 'repository.write', 'repository.admin') | |
1255 | @CSRFRequired() |
|
1258 | @CSRFRequired() | |
1256 | @view_config( |
|
1259 | @view_config( | |
1257 | route_name='pullrequest_comment_delete', request_method='POST', |
|
1260 | route_name='pullrequest_comment_delete', request_method='POST', | |
1258 | renderer='json_ext') |
|
1261 | renderer='json_ext') | |
1259 | def pull_request_comment_delete(self): |
|
1262 | def pull_request_comment_delete(self): | |
1260 | pull_request = PullRequest.get_or_404( |
|
1263 | pull_request = PullRequest.get_or_404( | |
1261 | self.request.matchdict['pull_request_id']) |
|
1264 | self.request.matchdict['pull_request_id']) | |
1262 |
|
1265 | |||
1263 | comment = ChangesetComment.get_or_404( |
|
1266 | comment = ChangesetComment.get_or_404( | |
1264 | self.request.matchdict['comment_id']) |
|
1267 | self.request.matchdict['comment_id']) | |
1265 | comment_id = comment.comment_id |
|
1268 | comment_id = comment.comment_id | |
1266 |
|
1269 | |||
1267 | if pull_request.is_closed(): |
|
1270 | if pull_request.is_closed(): | |
1268 | log.debug('comment: forbidden because pull request is closed') |
|
1271 | log.debug('comment: forbidden because pull request is closed') | |
1269 | raise HTTPForbidden() |
|
1272 | raise HTTPForbidden() | |
1270 |
|
1273 | |||
1271 | if not comment: |
|
1274 | if not comment: | |
1272 | log.debug('Comment with id:%s not found, skipping', comment_id) |
|
1275 | log.debug('Comment with id:%s not found, skipping', comment_id) | |
1273 | # comment already deleted in another call probably |
|
1276 | # comment already deleted in another call probably | |
1274 | return True |
|
1277 | return True | |
1275 |
|
1278 | |||
1276 | if comment.pull_request.is_closed(): |
|
1279 | if comment.pull_request.is_closed(): | |
1277 | # don't allow deleting comments on closed pull request |
|
1280 | # don't allow deleting comments on closed pull request | |
1278 | raise HTTPForbidden() |
|
1281 | raise HTTPForbidden() | |
1279 |
|
1282 | |||
1280 | is_repo_admin = h.HasRepoPermissionAny('repository.admin')(self.db_repo_name) |
|
1283 | is_repo_admin = h.HasRepoPermissionAny('repository.admin')(self.db_repo_name) | |
1281 | super_admin = h.HasPermissionAny('hg.admin')() |
|
1284 | super_admin = h.HasPermissionAny('hg.admin')() | |
1282 | comment_owner = comment.author.user_id == self._rhodecode_user.user_id |
|
1285 | comment_owner = comment.author.user_id == self._rhodecode_user.user_id | |
1283 | is_repo_comment = comment.repo.repo_name == self.db_repo_name |
|
1286 | is_repo_comment = comment.repo.repo_name == self.db_repo_name | |
1284 | comment_repo_admin = is_repo_admin and is_repo_comment |
|
1287 | comment_repo_admin = is_repo_admin and is_repo_comment | |
1285 |
|
1288 | |||
1286 | if super_admin or comment_owner or comment_repo_admin: |
|
1289 | if super_admin or comment_owner or comment_repo_admin: | |
1287 | old_calculated_status = comment.pull_request.calculated_review_status() |
|
1290 | old_calculated_status = comment.pull_request.calculated_review_status() | |
1288 | CommentsModel().delete(comment=comment, auth_user=self._rhodecode_user) |
|
1291 | CommentsModel().delete(comment=comment, auth_user=self._rhodecode_user) | |
1289 | Session().commit() |
|
1292 | Session().commit() | |
1290 | calculated_status = comment.pull_request.calculated_review_status() |
|
1293 | calculated_status = comment.pull_request.calculated_review_status() | |
1291 | if old_calculated_status != calculated_status: |
|
1294 | if old_calculated_status != calculated_status: | |
1292 | PullRequestModel()._trigger_pull_request_hook( |
|
1295 | PullRequestModel()._trigger_pull_request_hook( | |
1293 | comment.pull_request, self._rhodecode_user, 'review_status_change') |
|
1296 | comment.pull_request, self._rhodecode_user, 'review_status_change') | |
1294 | return True |
|
1297 | return True | |
1295 | else: |
|
1298 | else: | |
1296 | log.warning('No permissions for user %s to delete comment_id: %s', |
|
1299 | log.warning('No permissions for user %s to delete comment_id: %s', | |
1297 | self._rhodecode_db_user, comment_id) |
|
1300 | self._rhodecode_db_user, comment_id) | |
1298 | raise HTTPNotFound() |
|
1301 | raise HTTPNotFound() |
@@ -1,644 +1,684 b'' | |||||
1 | // navigation.less |
|
1 | // navigation.less | |
2 | // For use in RhodeCode applications; |
|
2 | // For use in RhodeCode applications; | |
3 | // see style guide documentation for guidelines. |
|
3 | // see style guide documentation for guidelines. | |
4 |
|
4 | |||
5 | // HEADER NAVIGATION |
|
5 | // HEADER NAVIGATION | |
6 |
|
6 | |||
7 | .horizontal-list { |
|
7 | .horizontal-list { | |
8 | float: right; |
|
8 | float: right; | |
9 | display: block; |
|
9 | display: block; | |
10 | margin: 0; |
|
10 | margin: 0; | |
11 | padding: 0; |
|
11 | padding: 0; | |
12 | -webkit-padding-start: 0; |
|
12 | -webkit-padding-start: 0; | |
13 | text-align: left; |
|
13 | text-align: left; | |
14 | font-size: @navigation-fontsize; |
|
14 | font-size: @navigation-fontsize; | |
15 | color: @grey6; |
|
15 | color: @grey6; | |
16 | z-index:10; |
|
16 | z-index:10; | |
17 |
|
17 | |||
18 | li { |
|
18 | li { | |
19 | line-height: 1em; |
|
19 | line-height: 1em; | |
20 | list-style-type: none; |
|
20 | list-style-type: none; | |
21 |
|
21 | |||
22 | a { |
|
22 | a { | |
23 | padding: 0 .5em; |
|
23 | padding: 0 .5em; | |
24 |
|
24 | |||
25 | &.menu_link_notifications { |
|
25 | &.menu_link_notifications { | |
26 | .pill(7px,@rcblue); |
|
26 | .pill(7px,@rcblue); | |
27 | display: inline; |
|
27 | display: inline; | |
28 | margin: 0 7px 0 .7em; |
|
28 | margin: 0 7px 0 .7em; | |
29 | font-size: @basefontsize; |
|
29 | font-size: @basefontsize; | |
30 | color: white; |
|
30 | color: white; | |
31 |
|
31 | |||
32 | &.empty { |
|
32 | &.empty { | |
33 | background-color: @grey4; |
|
33 | background-color: @grey4; | |
34 | } |
|
34 | } | |
35 |
|
35 | |||
36 | &:hover { |
|
36 | &:hover { | |
37 | background-color: @rcdarkblue; |
|
37 | background-color: @rcdarkblue; | |
38 | } |
|
38 | } | |
39 | } |
|
39 | } | |
40 | } |
|
40 | } | |
41 | .pill_container { |
|
41 | .pill_container { | |
42 | margin: 1.25em 0px 0px 0px; |
|
42 | margin: 1.25em 0px 0px 0px; | |
43 | float: right; |
|
43 | float: right; | |
44 | } |
|
44 | } | |
45 |
|
45 | |||
46 | &#quick_login_li { |
|
46 | &#quick_login_li { | |
47 | &:hover { |
|
47 | &:hover { | |
48 | color: @grey5; |
|
48 | color: @grey5; | |
49 | } |
|
49 | } | |
50 |
|
50 | |||
51 | a.menu_link_notifications { |
|
51 | a.menu_link_notifications { | |
52 | color: white; |
|
52 | color: white; | |
53 | } |
|
53 | } | |
54 |
|
54 | |||
55 | .user { |
|
55 | .user { | |
56 | padding-bottom: 10px; |
|
56 | padding-bottom: 10px; | |
57 | } |
|
57 | } | |
58 |
|
58 | |||
59 | &.open { |
|
59 | &.open { | |
60 | .user { |
|
60 | .user { | |
61 | border-bottom: 5px solid @rcblue; |
|
61 | border-bottom: 5px solid @rcblue; | |
62 | } |
|
62 | } | |
63 | } |
|
63 | } | |
64 | } |
|
64 | } | |
65 |
|
65 | |||
66 | &:before { content: none; } |
|
66 | &:before { content: none; } | |
67 |
|
67 | |||
68 | &:last-child { |
|
68 | &:last-child { | |
69 | .menulabel { |
|
69 | .menulabel { | |
70 | padding-right: 0; |
|
70 | padding-right: 0; | |
71 | border-right: none; |
|
71 | border-right: none; | |
72 |
|
72 | |||
73 | .show_more { |
|
73 | .show_more { | |
74 | padding-right: 0; |
|
74 | padding-right: 0; | |
75 | } |
|
75 | } | |
76 | } |
|
76 | } | |
77 |
|
77 | |||
78 | &> a { |
|
78 | &> a { | |
79 | border-bottom: none; |
|
79 | border-bottom: none; | |
80 | } |
|
80 | } | |
81 | } |
|
81 | } | |
82 |
|
82 | |||
83 | &.active { |
|
83 | &.active { | |
84 | border-bottom: 5px solid @rcblue; |
|
84 | border-bottom: 5px solid @rcblue; | |
85 | } |
|
85 | } | |
86 |
|
86 | |||
87 | &.open { |
|
87 | &.open { | |
88 |
|
88 | |||
89 | a { |
|
89 | a { | |
90 | color: white; |
|
90 | color: white; | |
91 | } |
|
91 | } | |
92 | } |
|
92 | } | |
93 |
|
93 | |||
94 | &:focus { |
|
94 | &:focus { | |
95 | outline: none; |
|
95 | outline: none; | |
96 | } |
|
96 | } | |
97 |
|
97 | |||
98 | ul li { |
|
98 | ul li { | |
99 | display: block; |
|
99 | display: block; | |
100 |
|
100 | |||
101 | &:last-child> a { |
|
101 | &:last-child> a { | |
102 | border-bottom: none; |
|
102 | border-bottom: none; | |
103 | } |
|
103 | } | |
104 |
|
104 | |||
105 | ul li:last-child a { |
|
105 | ul li:last-child a { | |
106 | /* we don't expect more then 3 levels of submenu and the third |
|
106 | /* we don't expect more then 3 levels of submenu and the third | |
107 | level can have different html structure */ |
|
107 | level can have different html structure */ | |
108 | border-bottom: none; |
|
108 | border-bottom: none; | |
109 | } |
|
109 | } | |
110 | } |
|
110 | } | |
111 | } |
|
111 | } | |
112 |
|
112 | |||
113 | > li { |
|
113 | > li { | |
114 | float: left; |
|
114 | float: left; | |
115 | display: block; |
|
115 | display: block; | |
116 | padding: 0; |
|
116 | padding: 0; | |
117 |
|
117 | |||
118 | > a, |
|
118 | > a, | |
119 | &.has_select2 a { |
|
119 | &.has_select2 a { | |
120 | display: block; |
|
120 | display: block; | |
121 | padding: 10px 0 2px; |
|
121 | padding: 10px 0 2px; | |
122 |
|
122 | |||
123 | .show_more { |
|
123 | .show_more { | |
124 | margin-top: -4px; |
|
124 | margin-top: -4px; | |
125 | padding-right: .5em; |
|
125 | padding-right: .5em; | |
126 | } |
|
126 | } | |
127 | } |
|
127 | } | |
128 |
|
128 | |||
129 | .menulabel { |
|
129 | .menulabel { | |
130 | padding: 0 .5em; |
|
130 | padding: 0 .5em; | |
131 | line-height: 1em; |
|
131 | line-height: 1em; | |
132 | // for this specifically we do not use a variable |
|
132 | // for this specifically we do not use a variable | |
133 | border-right: 1px solid @grey4; |
|
133 | border-right: 1px solid @grey4; | |
134 | } |
|
134 | } | |
135 |
|
135 | |||
136 | .pr_notifications { |
|
136 | .pr_notifications { | |
137 | padding-left: .5em; |
|
137 | padding-left: .5em; | |
138 | } |
|
138 | } | |
139 |
|
139 | |||
140 | .pr_notifications + .menulabel { |
|
140 | .pr_notifications + .menulabel { | |
141 | display:inline; |
|
141 | display:inline; | |
142 | padding-left: 0; |
|
142 | padding-left: 0; | |
143 | } |
|
143 | } | |
144 |
|
144 | |||
145 | &:hover, |
|
145 | &:hover, | |
146 | &.open, |
|
146 | &.open, | |
147 | &.active { |
|
147 | &.active { | |
148 | a { |
|
148 | a { | |
149 | color: @grey1; |
|
149 | color: @grey1; | |
150 | } |
|
150 | } | |
151 | } |
|
151 | } | |
152 | } |
|
152 | } | |
153 |
|
153 | |||
154 | pre { |
|
154 | pre { | |
155 | margin: 0; |
|
155 | margin: 0; | |
156 | padding: 0; |
|
156 | padding: 0; | |
157 | } |
|
157 | } | |
158 |
|
158 | |||
159 | .select2-container, |
|
159 | .select2-container, | |
160 | .menulink.childs { |
|
160 | .menulink.childs { | |
161 | position: relative; |
|
161 | position: relative; | |
162 | } |
|
162 | } | |
163 |
|
163 | |||
164 | #quick_login { |
|
164 | #quick_login { | |
165 |
|
165 | |||
166 | li a { |
|
166 | li a { | |
167 | padding: .5em 0; |
|
167 | padding: .5em 0; | |
168 | border-bottom: none; |
|
168 | border-bottom: none; | |
169 | color: @grey2; |
|
169 | color: @grey2; | |
170 |
|
170 | |||
171 | &:hover { color: @grey1; } |
|
171 | &:hover { color: @grey1; } | |
172 | } |
|
172 | } | |
173 |
|
173 | |||
174 | .show_more { |
|
174 | .show_more { | |
175 | padding-left: .5em; |
|
175 | padding-left: .5em; | |
176 | } |
|
176 | } | |
177 | } |
|
177 | } | |
178 |
|
178 | |||
179 | #quick_login_link { |
|
179 | #quick_login_link { | |
180 | display: inline-block; |
|
180 | display: inline-block; | |
181 |
|
181 | |||
182 | .gravatar { |
|
182 | .gravatar { | |
183 | border: 1px solid @grey2; |
|
183 | border: 1px solid @grey2; | |
184 | } |
|
184 | } | |
185 |
|
185 | |||
186 | .gravatar-login { |
|
186 | .gravatar-login { | |
187 | height: 20px; |
|
187 | height: 20px; | |
188 | width: 20px; |
|
188 | width: 20px; | |
189 | margin: -8px 0; |
|
189 | margin: -8px 0; | |
190 | padding: 0; |
|
190 | padding: 0; | |
191 | } |
|
191 | } | |
192 |
|
192 | |||
193 | &:hover .user { |
|
193 | &:hover .user { | |
194 | color: @grey6; |
|
194 | color: @grey6; | |
195 | } |
|
195 | } | |
196 | } |
|
196 | } | |
197 | } |
|
197 | } | |
198 | .header .horizontal-list { |
|
198 | .header .horizontal-list { | |
199 |
|
199 | |||
200 | li { |
|
200 | li { | |
201 |
|
201 | |||
202 | &#quick_login_li { |
|
202 | &#quick_login_li { | |
203 | padding-left: .5em; |
|
203 | padding-left: .5em; | |
204 |
|
204 | |||
205 | &:hover #quick_login_link { |
|
205 | &:hover #quick_login_link { | |
206 | color: inherit; |
|
206 | color: inherit; | |
207 | } |
|
207 | } | |
208 | } |
|
208 | } | |
209 |
|
209 | |||
210 | &:before { content: none; } |
|
210 | &:before { content: none; } | |
211 | } |
|
211 | } | |
212 |
|
212 | |||
213 | > li { |
|
213 | > li { | |
214 |
|
214 | |||
215 | a { |
|
215 | a { | |
216 | padding: 18px 0 12px 0; |
|
216 | padding: 18px 0 12px 0; | |
217 | color: @nav-grey; |
|
217 | color: @nav-grey; | |
218 |
|
218 | |||
219 | &.menu_link_notifications { |
|
219 | &.menu_link_notifications { | |
220 | padding: 1px 8px; |
|
220 | padding: 1px 8px; | |
221 | } |
|
221 | } | |
222 | } |
|
222 | } | |
223 |
|
223 | |||
224 | &:hover, |
|
224 | &:hover, | |
225 | &.open, |
|
225 | &.open, | |
226 | &.active { |
|
226 | &.active { | |
227 | .pill_container a { |
|
227 | .pill_container a { | |
228 | // don't select text for the pill container, it has it' own |
|
228 | // don't select text for the pill container, it has it' own | |
229 | // hover behaviour |
|
229 | // hover behaviour | |
230 | color: @nav-grey; |
|
230 | color: @nav-grey; | |
231 | } |
|
231 | } | |
232 | } |
|
232 | } | |
233 |
|
233 | |||
234 | &:hover, |
|
234 | &:hover, | |
235 | &.open, |
|
235 | &.open, | |
236 | &.active { |
|
236 | &.active { | |
237 | a { |
|
237 | a { | |
238 | color: @grey6; |
|
238 | color: @grey6; | |
239 | } |
|
239 | } | |
240 | } |
|
240 | } | |
241 |
|
241 | |||
242 | .select2-dropdown-open a { |
|
242 | .select2-dropdown-open a { | |
243 | color: @grey6; |
|
243 | color: @grey6; | |
244 | } |
|
244 | } | |
245 |
|
245 | |||
246 | .repo-switcher { |
|
246 | .repo-switcher { | |
247 | padding-left: 0; |
|
247 | padding-left: 0; | |
248 |
|
248 | |||
249 | .menulabel { |
|
249 | .menulabel { | |
250 | padding-left: 0; |
|
250 | padding-left: 0; | |
251 | } |
|
251 | } | |
252 | } |
|
252 | } | |
253 | } |
|
253 | } | |
254 |
|
254 | |||
255 | li ul li { |
|
255 | li ul li { | |
256 | background-color:@grey2; |
|
256 | background-color:@grey2; | |
257 |
|
257 | |||
258 | a { |
|
258 | a { | |
259 | padding: .5em 0; |
|
259 | padding: .5em 0; | |
260 | border-bottom: @border-thickness solid @border-default-color; |
|
260 | border-bottom: @border-thickness solid @border-default-color; | |
261 | color: @grey6; |
|
261 | color: @grey6; | |
262 | } |
|
262 | } | |
263 |
|
263 | |||
264 | &:last-child a, &.last a{ |
|
264 | &:last-child a, &.last a{ | |
265 | border-bottom: none; |
|
265 | border-bottom: none; | |
266 | } |
|
266 | } | |
267 |
|
267 | |||
268 | &:hover { |
|
268 | &:hover { | |
269 | background-color: @grey3; |
|
269 | background-color: @grey3; | |
270 | } |
|
270 | } | |
271 | } |
|
271 | } | |
272 |
|
272 | |||
273 | .submenu { |
|
273 | .submenu { | |
274 | margin-top: 5px; |
|
274 | margin-top: 5px; | |
275 | } |
|
275 | } | |
276 | } |
|
276 | } | |
277 |
|
277 | |||
278 | // SUBMENUS |
|
278 | // SUBMENUS | |
279 | .navigation .submenu { |
|
279 | .navigation .submenu { | |
280 | display: none; |
|
280 | display: none; | |
281 | } |
|
281 | } | |
282 |
|
282 | |||
283 | .navigation li.open { |
|
283 | .navigation li.open { | |
284 |
|
284 | .submenu { | ||
285 | .submenu, |
|
|||
286 | .repo_switcher { |
|
|||
287 | display: block; |
|
285 | display: block; | |
288 | } |
|
286 | } | |
289 | } |
|
287 | } | |
290 |
|
288 | |||
291 | .navigation li:last-child .submenu { |
|
289 | .navigation li:last-child .submenu { | |
292 | right: -20px; |
|
290 | right: -20px; | |
293 | left: auto; |
|
291 | left: auto; | |
294 | } |
|
292 | } | |
295 |
|
293 | |||
296 | .submenu { |
|
294 | .submenu { | |
297 | position: absolute; |
|
295 | position: absolute; | |
298 | top: 100%; |
|
296 | top: 100%; | |
299 | left: 0; |
|
297 | left: 0; | |
300 | min-width: 150px; |
|
298 | min-width: 150px; | |
301 | margin: 6px 0 0; |
|
299 | margin: 6px 0 0; | |
302 | padding: 0; |
|
300 | padding: 0; | |
303 | text-align: left; |
|
301 | text-align: left; | |
304 | font-family: @text-light; |
|
302 | font-family: @text-light; | |
305 | border-radius: @border-radius; |
|
303 | border-radius: @border-radius; | |
306 | z-index: 20; |
|
304 | z-index: 20; | |
307 |
|
305 | |||
308 | li { |
|
306 | li { | |
309 | display: block; |
|
307 | display: block; | |
310 | margin: 0; |
|
308 | margin: 0; | |
311 | padding: 0 .5em; |
|
309 | padding: 0 .5em; | |
312 | line-height: 1em; |
|
310 | line-height: 1em; | |
313 | color: @grey3; |
|
311 | color: @grey3; | |
314 | background-color: @grey6; |
|
312 | background-color: @grey6; | |
315 |
|
313 | |||
316 | &:before { content: none; } |
|
314 | &:before { content: none; } | |
317 |
|
315 | |||
318 | a { |
|
316 | a { | |
319 | display: block; |
|
317 | display: block; | |
320 | width: 100%; |
|
318 | width: 100%; | |
321 | padding: .5em 0; |
|
319 | padding: .5em 0; | |
322 | border-right: none; |
|
320 | border-right: none; | |
323 | border-bottom: @border-thickness solid white; |
|
321 | border-bottom: @border-thickness solid white; | |
324 | color: @grey3; |
|
322 | color: @grey3; | |
325 | } |
|
323 | } | |
326 |
|
324 | |||
327 | ul { |
|
325 | ul { | |
328 | display: none; |
|
326 | display: none; | |
329 | position: absolute; |
|
327 | position: absolute; | |
330 | top: 0; |
|
328 | top: 0; | |
331 | right: 100%; |
|
329 | right: 100%; | |
332 | padding: 0; |
|
330 | padding: 0; | |
333 | z-index: 30; |
|
331 | z-index: 30; | |
334 | } |
|
332 | } | |
335 | &:hover { |
|
333 | &:hover { | |
336 | background-color: @grey5; |
|
334 | background-color: @grey5; | |
337 | -webkit-transition: background .3s; |
|
335 | -webkit-transition: background .3s; | |
338 | -moz-transition: background .3s; |
|
336 | -moz-transition: background .3s; | |
339 | -o-transition: background .3s; |
|
337 | -o-transition: background .3s; | |
340 | transition: background .3s; |
|
338 | transition: background .3s; | |
341 |
|
339 | |||
342 | ul { |
|
340 | ul { | |
343 | display: block; |
|
341 | display: block; | |
344 | } |
|
342 | } | |
345 | } |
|
343 | } | |
346 | } |
|
344 | } | |
347 | } |
|
345 | } | |
348 |
|
346 | |||
349 |
|
347 | |||
350 |
|
348 | |||
351 |
|
349 | |||
352 | // repo dropdown |
|
350 | // repo dropdown | |
353 | .quick_repo_menu { |
|
351 | .quick_repo_menu { | |
354 | width: 15px; |
|
352 | width: 15px; | |
355 | text-align: center; |
|
353 | text-align: center; | |
356 | position: relative; |
|
354 | position: relative; | |
357 | cursor: pointer; |
|
355 | cursor: pointer; | |
358 |
|
356 | |||
359 | div { |
|
357 | div { | |
360 | overflow: visible !important; |
|
358 | overflow: visible !important; | |
361 | } |
|
359 | } | |
362 |
|
360 | |||
363 | &.sorting { |
|
361 | &.sorting { | |
364 | cursor: auto; |
|
362 | cursor: auto; | |
365 | } |
|
363 | } | |
366 |
|
364 | |||
367 | &:hover { |
|
365 | &:hover { | |
368 | .menu_items_container { |
|
366 | .menu_items_container { | |
369 | position: absolute; |
|
367 | position: absolute; | |
370 | display: block; |
|
368 | display: block; | |
371 | } |
|
369 | } | |
372 | .menu_items { |
|
370 | .menu_items { | |
373 | display: block; |
|
371 | display: block; | |
374 | } |
|
372 | } | |
375 | } |
|
373 | } | |
376 |
|
374 | |||
377 | i { |
|
375 | i { | |
378 | margin: 0; |
|
376 | margin: 0; | |
379 | color: @grey4; |
|
377 | color: @grey4; | |
380 | } |
|
378 | } | |
381 |
|
379 | |||
382 | .menu_items_container { |
|
380 | .menu_items_container { | |
383 | position: absolute; |
|
381 | position: absolute; | |
384 | top: 0; |
|
382 | top: 0; | |
385 | left: 100%; |
|
383 | left: 100%; | |
386 | margin: 0; |
|
384 | margin: 0; | |
387 | padding: 0; |
|
385 | padding: 0; | |
388 | list-style: none; |
|
386 | list-style: none; | |
389 | background-color: @grey6; |
|
387 | background-color: @grey6; | |
390 | z-index: 999; |
|
388 | z-index: 999; | |
391 | text-align: left; |
|
389 | text-align: left; | |
392 |
|
390 | |||
393 | a { |
|
391 | a { | |
394 | color: @grey2; |
|
392 | color: @grey2; | |
395 | } |
|
393 | } | |
396 |
|
394 | |||
397 | ul.menu_items { |
|
395 | ul.menu_items { | |
398 | margin: 0; |
|
396 | margin: 0; | |
399 | padding: 0; |
|
397 | padding: 0; | |
400 | } |
|
398 | } | |
401 |
|
399 | |||
402 | li { |
|
400 | li { | |
403 | margin: 0; |
|
401 | margin: 0; | |
404 | padding: 0; |
|
402 | padding: 0; | |
405 | line-height: 1em; |
|
403 | line-height: 1em; | |
406 | list-style-type: none; |
|
404 | list-style-type: none; | |
407 |
|
405 | |||
408 | &:before { content: none; } |
|
406 | &:before { content: none; } | |
409 |
|
407 | |||
410 | a { |
|
408 | a { | |
411 | display: block; |
|
409 | display: block; | |
412 | height: 16px; |
|
410 | height: 16px; | |
413 | padding: 8px; //must add up to td height (28px) |
|
411 | padding: 8px; //must add up to td height (28px) | |
414 |
|
412 | |||
415 | &:hover { |
|
413 | &:hover { | |
416 | background-color: @grey5; |
|
414 | background-color: @grey5; | |
417 | -webkit-transition: background .3s; |
|
415 | -webkit-transition: background .3s; | |
418 | -moz-transition: background .3s; |
|
416 | -moz-transition: background .3s; | |
419 | -o-transition: background .3s; |
|
417 | -o-transition: background .3s; | |
420 | transition: background .3s; |
|
418 | transition: background .3s; | |
421 | } |
|
419 | } | |
422 | } |
|
420 | } | |
423 | } |
|
421 | } | |
424 | } |
|
422 | } | |
425 | } |
|
423 | } | |
426 |
|
424 | |||
427 | // Header Repository Switcher |
|
425 | // Header Repository Switcher | |
428 | // Select2 Dropdown |
|
426 | // Select2 Dropdown | |
429 | #select2-drop.select2-drop.repo-switcher-dropdown { |
|
427 | #select2-drop.select2-drop.repo-switcher-dropdown { | |
430 | width: auto !important; |
|
428 | width: auto !important; | |
431 | margin-top: 5px; |
|
429 | margin-top: 5px; | |
432 | padding: 1em 0; |
|
430 | padding: 1em 0; | |
433 | text-align: left; |
|
431 | text-align: left; | |
434 | .border-radius-bottom(@border-radius); |
|
432 | .border-radius-bottom(@border-radius); | |
435 | border-color: transparent; |
|
433 | border-color: transparent; | |
436 | color: @grey6; |
|
434 | color: @grey6; | |
437 | background-color: @grey2; |
|
435 | background-color: @grey2; | |
438 |
|
436 | |||
439 | input { |
|
437 | input { | |
440 | min-width: 90%; |
|
438 | min-width: 90%; | |
441 | } |
|
439 | } | |
442 |
|
440 | |||
443 | ul.select2-result-sub { |
|
441 | ul.select2-result-sub { | |
444 |
|
442 | |||
445 | li { |
|
443 | li { | |
446 | line-height: 1em; |
|
444 | line-height: 1em; | |
447 |
|
445 | |||
448 | &:hover, |
|
446 | &:hover, | |
449 | &.select2-highlighted { |
|
447 | &.select2-highlighted { | |
450 | background-color: @grey3; |
|
448 | background-color: @grey3; | |
451 | } |
|
449 | } | |
452 | } |
|
450 | } | |
453 |
|
451 | |||
454 | &:before { content: none; } |
|
452 | &:before { content: none; } | |
455 | } |
|
453 | } | |
456 |
|
454 | |||
457 | ul.select2-results { |
|
455 | ul.select2-results { | |
458 | min-width: 200px; |
|
456 | min-width: 200px; | |
459 | margin: 0; |
|
457 | margin: 0; | |
460 | padding: 0; |
|
458 | padding: 0; | |
461 | list-style-type: none; |
|
459 | list-style-type: none; | |
462 | overflow-x: visible; |
|
460 | overflow-x: visible; | |
463 | overflow-y: scroll; |
|
461 | overflow-y: scroll; | |
464 |
|
462 | |||
465 | li { |
|
463 | li { | |
466 | padding: 0 8px; |
|
464 | padding: 0 8px; | |
467 | line-height: 1em; |
|
465 | line-height: 1em; | |
468 | color: @grey6; |
|
466 | color: @grey6; | |
469 |
|
467 | |||
470 | &:before { content: none; } |
|
468 | &:before { content: none; } | |
471 |
|
469 | |||
472 | &>.select2-result-label { |
|
470 | &>.select2-result-label { | |
473 | padding: 8px 0; |
|
471 | padding: 8px 0; | |
474 | border-bottom: @border-thickness solid @grey3; |
|
472 | border-bottom: @border-thickness solid @grey3; | |
475 | white-space: nowrap; |
|
473 | white-space: nowrap; | |
476 | color: @grey5; |
|
474 | color: @grey5; | |
477 | cursor: pointer; |
|
475 | cursor: pointer; | |
478 | } |
|
476 | } | |
479 |
|
477 | |||
480 | &.select2-result-with-children { |
|
478 | &.select2-result-with-children { | |
481 | margin: 0; |
|
479 | margin: 0; | |
482 | padding: 0; |
|
480 | padding: 0; | |
483 | } |
|
481 | } | |
484 |
|
482 | |||
485 | &.select2-result-unselectable > .select2-result-label { |
|
483 | &.select2-result-unselectable > .select2-result-label { | |
486 | margin: 0 8px; |
|
484 | margin: 0 8px; | |
487 | } |
|
485 | } | |
488 |
|
486 | |||
489 | } |
|
487 | } | |
490 | } |
|
488 | } | |
491 |
|
489 | |||
492 | ul.select2-result-sub { |
|
490 | ul.select2-result-sub { | |
493 | margin: 0; |
|
491 | margin: 0; | |
494 | padding: 0; |
|
492 | padding: 0; | |
495 |
|
493 | |||
496 | li { |
|
494 | li { | |
497 | display: block; |
|
495 | display: block; | |
498 | margin: 0; |
|
496 | margin: 0; | |
499 | border-right: none; |
|
497 | border-right: none; | |
500 | line-height: 1em; |
|
498 | line-height: 1em; | |
501 | font-family: @text-light; |
|
499 | font-family: @text-light; | |
502 | color: @grey2; |
|
500 | color: @grey2; | |
503 |
|
501 | |||
504 | &:before { content: none; } |
|
502 | &:before { content: none; } | |
505 |
|
503 | |||
506 | &:hover { |
|
504 | &:hover { | |
507 | background-color: @grey3; |
|
505 | background-color: @grey3; | |
508 | } |
|
506 | } | |
509 | } |
|
507 | } | |
510 | } |
|
508 | } | |
511 | } |
|
509 | } | |
512 |
|
510 | |||
513 |
|
511 | |||
514 | #context-bar { |
|
512 | #context-bar { | |
515 | display: block; |
|
513 | display: block; | |
516 | margin: 0 auto; |
|
514 | margin: 0 auto; | |
517 | padding: 0 @header-padding; |
|
515 | padding: 0 @header-padding; | |
518 | background-color: @grey6; |
|
516 | background-color: @grey6; | |
519 | border-bottom: @border-thickness solid @grey5; |
|
517 | border-bottom: @border-thickness solid @grey5; | |
520 |
|
518 | |||
521 | .clear { |
|
519 | .clear { | |
522 | clear: both; |
|
520 | clear: both; | |
523 | } |
|
521 | } | |
524 | } |
|
522 | } | |
525 |
|
523 | |||
526 | ul#context-pages { |
|
524 | ul#context-pages { | |
527 | li { |
|
525 | li { | |
528 | line-height: 1em; |
|
526 | line-height: 1em; | |
529 |
|
527 | |||
530 | &:before { content: none; } |
|
528 | &:before { content: none; } | |
531 |
|
529 | |||
532 | a { |
|
530 | a { | |
533 | color: @grey3; |
|
531 | color: @grey3; | |
534 | } |
|
532 | } | |
535 |
|
533 | |||
536 | &.active { |
|
534 | &.active { | |
537 | // special case, non-variable color |
|
535 | // special case, non-variable color | |
538 | border-bottom: 4px solid @nav-grey; |
|
536 | border-bottom: 4px solid @nav-grey; | |
539 |
|
537 | |||
540 | a { |
|
538 | a { | |
541 | color: @grey1; |
|
539 | color: @grey1; | |
542 | } |
|
540 | } | |
543 | } |
|
541 | } | |
544 | } |
|
542 | } | |
545 | } |
|
543 | } | |
546 |
|
544 | |||
547 | // PAGINATION |
|
545 | // PAGINATION | |
548 |
|
546 | |||
549 | .pagination { |
|
547 | .pagination { | |
550 | border: @border-thickness solid @rcblue; |
|
548 | border: @border-thickness solid @rcblue; | |
551 | color: @rcblue; |
|
549 | color: @rcblue; | |
552 |
|
550 | |||
553 | .current { |
|
551 | .current { | |
554 | color: @grey4; |
|
552 | color: @grey4; | |
555 | } |
|
553 | } | |
556 | } |
|
554 | } | |
557 |
|
555 | |||
558 | .dataTables_processing { |
|
556 | .dataTables_processing { | |
559 | text-align: center; |
|
557 | text-align: center; | |
560 | font-size: 1.1em; |
|
558 | font-size: 1.1em; | |
561 | position: relative; |
|
559 | position: relative; | |
562 | top: 95px; |
|
560 | top: 95px; | |
563 | } |
|
561 | } | |
564 |
|
562 | |||
565 | .dataTables_paginate, .pagination-wh { |
|
563 | .dataTables_paginate, .pagination-wh { | |
566 | text-align: left; |
|
564 | text-align: left; | |
567 | display: inline-block; |
|
565 | display: inline-block; | |
568 | border-left: 1px solid @rcblue; |
|
566 | border-left: 1px solid @rcblue; | |
569 | float: none; |
|
567 | float: none; | |
570 | overflow: hidden; |
|
568 | overflow: hidden; | |
571 |
|
569 | |||
572 | .paginate_button, .pager_curpage, |
|
570 | .paginate_button, .pager_curpage, | |
573 | .pager_link, .pg-previous, .pg-next, .pager_dotdot { |
|
571 | .pager_link, .pg-previous, .pg-next, .pager_dotdot { | |
574 | display: inline-block; |
|
572 | display: inline-block; | |
575 | padding: @menupadding/4 @menupadding; |
|
573 | padding: @menupadding/4 @menupadding; | |
576 | border: 1px solid @rcblue; |
|
574 | border: 1px solid @rcblue; | |
577 | border-left: 0; |
|
575 | border-left: 0; | |
578 | color: @rcblue; |
|
576 | color: @rcblue; | |
579 | cursor: pointer; |
|
577 | cursor: pointer; | |
580 | float: left; |
|
578 | float: left; | |
581 | } |
|
579 | } | |
582 |
|
580 | |||
583 | .pager_curpage, .pager_dotdot, |
|
581 | .pager_curpage, .pager_dotdot, | |
584 | .paginate_button.current, .paginate_button.disabled, |
|
582 | .paginate_button.current, .paginate_button.disabled, | |
585 | .disabled { |
|
583 | .disabled { | |
586 | color: @grey3; |
|
584 | color: @grey3; | |
587 | cursor: default; |
|
585 | cursor: default; | |
588 | } |
|
586 | } | |
589 |
|
587 | |||
590 | .ellipsis { |
|
588 | .ellipsis { | |
591 | display: inline-block; |
|
589 | display: inline-block; | |
592 | text-align: left; |
|
590 | text-align: left; | |
593 | padding: @menupadding/4 @menupadding; |
|
591 | padding: @menupadding/4 @menupadding; | |
594 | border: 1px solid @rcblue; |
|
592 | border: 1px solid @rcblue; | |
595 | border-left: 0; |
|
593 | border-left: 0; | |
596 | float: left; |
|
594 | float: left; | |
597 | } |
|
595 | } | |
598 | } |
|
596 | } | |
599 |
|
597 | |||
600 | // SIDEBAR |
|
598 | // SIDEBAR | |
601 |
|
599 | |||
602 | .sidebar { |
|
600 | .sidebar { | |
603 | .block-left; |
|
601 | .block-left; | |
604 | clear: left; |
|
602 | clear: left; | |
605 | max-width: @sidebar-width; |
|
603 | max-width: @sidebar-width; | |
606 | margin-right: @sidebarpadding; |
|
604 | margin-right: @sidebarpadding; | |
607 | padding-right: @sidebarpadding; |
|
605 | padding-right: @sidebarpadding; | |
608 | font-family: @text-regular; |
|
606 | font-family: @text-regular; | |
609 | color: @grey1; |
|
607 | color: @grey1; | |
610 |
|
608 | |||
611 | &#graph_nodes { |
|
609 | &#graph_nodes { | |
612 | clear:both; |
|
610 | clear:both; | |
613 | width: auto; |
|
611 | width: auto; | |
614 | margin-left: -100px; |
|
612 | margin-left: -100px; | |
615 | padding: 0; |
|
613 | padding: 0; | |
616 | border: none; |
|
614 | border: none; | |
617 | } |
|
615 | } | |
618 |
|
616 | |||
619 | .nav-pills { |
|
617 | .nav-pills { | |
620 | margin: 0; |
|
618 | margin: 0; | |
621 | } |
|
619 | } | |
622 |
|
620 | |||
623 | .nav { |
|
621 | .nav { | |
624 | list-style: none; |
|
622 | list-style: none; | |
625 | padding: 0; |
|
623 | padding: 0; | |
626 |
|
624 | |||
627 | li { |
|
625 | li { | |
628 | padding-bottom: @menupadding; |
|
626 | padding-bottom: @menupadding; | |
629 | line-height: 1em; |
|
627 | line-height: 1em; | |
630 | color: @grey4; |
|
628 | color: @grey4; | |
631 |
|
629 | |||
632 | &.active a { |
|
630 | &.active a { | |
633 | color: @grey2; |
|
631 | color: @grey2; | |
634 | } |
|
632 | } | |
635 |
|
633 | |||
636 | a { |
|
634 | a { | |
637 | color: @grey4; |
|
635 | color: @grey4; | |
638 | } |
|
636 | } | |
639 |
|
637 | |||
640 | &:before { content: none; } |
|
638 | &:before { content: none; } | |
641 | } |
|
639 | } | |
642 |
|
640 | |||
643 | } |
|
641 | } | |
644 | } |
|
642 | } | |
|
643 | ||||
|
644 | .main_filter_help_box { | |||
|
645 | padding: 7px 7px; | |||
|
646 | border-top: 1px solid @grey4; | |||
|
647 | border-right: 1px solid @grey4; | |||
|
648 | border-bottom: 1px solid @grey4; | |||
|
649 | display: inline-block; | |||
|
650 | vertical-align: top; | |||
|
651 | margin-left: -5px; | |||
|
652 | background: @grey3; | |||
|
653 | } | |||
|
654 | ||||
|
655 | .main_filter_input_box { | |||
|
656 | display: inline-block; | |||
|
657 | } | |||
|
658 | ||||
|
659 | .main_filter_box { | |||
|
660 | margin: 9px 0 0 0; | |||
|
661 | } | |||
|
662 | ||||
|
663 | #main_filter_help { | |||
|
664 | background: @grey3; | |||
|
665 | border: 1px solid black; | |||
|
666 | position: absolute; | |||
|
667 | white-space: pre-wrap; | |||
|
668 | z-index: 9999; | |||
|
669 | color: @nav-grey; | |||
|
670 | margin: 1px 7px; | |||
|
671 | padding: 0 2px; | |||
|
672 | } | |||
|
673 | ||||
|
674 | .main_filter_input { | |||
|
675 | padding: 6px; | |||
|
676 | min-width: 220px; | |||
|
677 | color: @nav-grey; | |||
|
678 | background: @grey3; | |||
|
679 | } | |||
|
680 | ||||
|
681 | .main_filter_input::placeholder { | |||
|
682 | color: @nav-grey; | |||
|
683 | opacity: 1; | |||
|
684 | } |
@@ -1,545 +1,545 b'' | |||||
1 | // |
|
1 | // | |
2 | // Typography |
|
2 | // Typography | |
3 | // modified from Bootstrap |
|
3 | // modified from Bootstrap | |
4 | // -------------------------------------------------- |
|
4 | // -------------------------------------------------- | |
5 |
|
5 | |||
6 | // Base |
|
6 | // Base | |
7 | body { |
|
7 | body { | |
8 | font-size: @basefontsize; |
|
8 | font-size: @basefontsize; | |
9 | font-family: @text-light; |
|
9 | font-family: @text-light; | |
10 | letter-spacing: .02em; |
|
10 | letter-spacing: .02em; | |
11 | color: @grey2; |
|
11 | color: @grey2; | |
12 | } |
|
12 | } | |
13 |
|
13 | |||
14 | #content, label{ |
|
14 | #content, label{ | |
15 | font-size: @basefontsize; |
|
15 | font-size: @basefontsize; | |
16 | } |
|
16 | } | |
17 |
|
17 | |||
18 | label { |
|
18 | label { | |
19 | color: @grey2; |
|
19 | color: @grey2; | |
20 | } |
|
20 | } | |
21 |
|
21 | |||
22 | ::selection { background: @rchighlightblue; } |
|
22 | ::selection { background: @rchighlightblue; } | |
23 |
|
23 | |||
24 | // Headings |
|
24 | // Headings | |
25 | // ------------------------- |
|
25 | // ------------------------- | |
26 |
|
26 | |||
27 | h1, h2, h3, h4, h5, h6, |
|
27 | h1, h2, h3, h4, h5, h6, | |
28 | .h1, .h2, .h3, .h4, .h5, .h6 { |
|
28 | .h1, .h2, .h3, .h4, .h5, .h6 { | |
29 | margin: 0 0 @textmargin 0; |
|
29 | margin: 0 0 @textmargin 0; | |
30 | padding: 0; |
|
30 | padding: 0; | |
31 | line-height: 1.8em; |
|
31 | line-height: 1.8em; | |
32 | color: @text-color; |
|
32 | color: @text-color; | |
33 | a { |
|
33 | a { | |
34 | color: @rcblue; |
|
34 | color: @rcblue; | |
35 | } |
|
35 | } | |
36 | } |
|
36 | } | |
37 |
|
37 | |||
38 | h1, .h1 { font-size: 1.54em; font-family: @text-bold; } |
|
38 | h1, .h1 { font-size: 1.54em; font-family: @text-bold; } | |
39 | h2, .h2 { font-size: 1.23em; font-family: @text-semibold; } |
|
39 | h2, .h2 { font-size: 1.23em; font-family: @text-semibold; } | |
40 | h3, .h3 { font-size: 1.23em; font-family: @text-regular; } |
|
40 | h3, .h3 { font-size: 1.23em; font-family: @text-regular; } | |
41 | h4, .h4 { font-size: 1em; font-family: @text-bold; } |
|
41 | h4, .h4 { font-size: 1em; font-family: @text-bold; } | |
42 | h5, .h5 { font-size: 1em; font-family: @text-bold-italic; } |
|
42 | h5, .h5 { font-size: 1em; font-family: @text-bold-italic; } | |
43 | h6, .h6 { font-size: 1em; font-family: @text-bold-italic; } |
|
43 | h6, .h6 { font-size: 1em; font-family: @text-bold-italic; } | |
44 |
|
44 | |||
45 | // Breadcrumbs |
|
45 | // Breadcrumbs | |
46 | .breadcrumbs { |
|
46 | .breadcrumbs { | |
47 | &:extend(h1); |
|
47 | &:extend(h1); | |
48 | margin: 0; |
|
48 | margin: 0; | |
49 | } |
|
49 | } | |
50 |
|
50 | |||
51 | .breadcrumbs_light { |
|
51 | .breadcrumbs_light { | |
52 | float:left; |
|
52 | float:left; | |
53 | font-size: 1.3em; |
|
53 | font-size: 1.3em; | |
54 | line-height: 38px; |
|
54 | line-height: 38px; | |
55 | } |
|
55 | } | |
56 |
|
56 | |||
57 | // Body text |
|
57 | // Body text | |
58 | // ------------------------- |
|
58 | // ------------------------- | |
59 |
|
59 | |||
60 | p { |
|
60 | p { | |
61 | margin: 0 0 @textmargin 0; |
|
61 | margin: 0 0 @textmargin 0; | |
62 | padding: 0; |
|
62 | padding: 0; | |
63 | line-height: 2em; |
|
63 | line-height: 2em; | |
64 | } |
|
64 | } | |
65 |
|
65 | |||
66 | .lead { |
|
66 | .lead { | |
67 | margin-bottom: @textmargin; |
|
67 | margin-bottom: @textmargin; | |
68 | font-weight: 300; |
|
68 | font-weight: 300; | |
69 | line-height: 1.4; |
|
69 | line-height: 1.4; | |
70 |
|
70 | |||
71 | @media (min-width: @screen-sm-min) { |
|
71 | @media (min-width: @screen-sm-min) { | |
72 | font-size: (@basefontsize * 1.5); |
|
72 | font-size: (@basefontsize * 1.5); | |
73 | } |
|
73 | } | |
74 | } |
|
74 | } | |
75 |
|
75 | |||
76 | a, |
|
76 | a, | |
77 | .link { |
|
77 | .link { | |
78 | color: @rcblue; |
|
78 | color: @rcblue; | |
79 | text-decoration: none; |
|
79 | text-decoration: none; | |
80 | outline: none; |
|
80 | outline: none; | |
81 | cursor: pointer; |
|
81 | cursor: pointer; | |
82 |
|
82 | |||
83 | &:focus { |
|
83 | &:focus { | |
84 | outline: none; |
|
84 | outline: none; | |
85 | } |
|
85 | } | |
86 |
|
86 | |||
87 | &:hover { |
|
87 | &:hover { | |
88 | color: @rcdarkblue; |
|
88 | color: @rcdarkblue; | |
89 | } |
|
89 | } | |
90 | } |
|
90 | } | |
91 |
|
91 | |||
92 | img { |
|
92 | img { | |
93 | border: none; |
|
93 | border: none; | |
94 | outline: none; |
|
94 | outline: none; | |
95 | } |
|
95 | } | |
96 |
|
96 | |||
97 | strong { |
|
97 | strong { | |
98 | font-family: @text-bold; |
|
98 | font-family: @text-bold; | |
99 | } |
|
99 | } | |
100 |
|
100 | |||
101 | em { |
|
101 | em { | |
102 | font-family: @text-italic; |
|
102 | font-family: @text-italic; | |
103 | } |
|
103 | } | |
104 |
|
104 | |||
105 | strong em, |
|
105 | strong em, | |
106 | em strong { |
|
106 | em strong { | |
107 | font-family: @text-bold-italic; |
|
107 | font-family: @text-bold-italic; | |
108 | } |
|
108 | } | |
109 |
|
109 | |||
110 | //TODO: lisa: b and i are depreciated, but we are still using them in places. |
|
110 | //TODO: lisa: b and i are depreciated, but we are still using them in places. | |
111 | // Should probably make some decision whether to keep or lose these. |
|
111 | // Should probably make some decision whether to keep or lose these. | |
112 | b { |
|
112 | b { | |
113 |
|
113 | |||
114 | } |
|
114 | } | |
115 |
|
115 | |||
116 | i { |
|
116 | i { | |
117 | font-style: normal; |
|
117 | font-style: normal; | |
118 | } |
|
118 | } | |
119 |
|
119 | |||
120 | label { |
|
120 | label { | |
121 | color: @text-color; |
|
121 | color: @text-color; | |
122 |
|
122 | |||
123 | input[type="checkbox"] { |
|
123 | input[type="checkbox"] { | |
124 | margin-right: 1em; |
|
124 | margin-right: 1em; | |
125 | } |
|
125 | } | |
126 | input[type="radio"] { |
|
126 | input[type="radio"] { | |
127 | margin-right: 1em; |
|
127 | margin-right: 1em; | |
128 | } |
|
128 | } | |
129 | } |
|
129 | } | |
130 |
|
130 | |||
131 | code, |
|
131 | code, | |
132 | .code { |
|
132 | .code { | |
133 | font-size: .95em; |
|
133 | font-size: .95em; | |
134 | font-family: @text-code; |
|
134 | font-family: @text-code; | |
135 | color: @grey3; |
|
135 | color: @grey3; | |
136 |
|
136 | |||
137 | a { |
|
137 | a { | |
138 | color: lighten(@rcblue,10%) |
|
138 | color: lighten(@rcblue,10%) | |
139 | } |
|
139 | } | |
140 | } |
|
140 | } | |
141 |
|
141 | |||
142 | pre { |
|
142 | pre { | |
143 | margin: 0; |
|
143 | margin: 0; | |
144 | padding: 0; |
|
144 | padding: 0; | |
145 | border: 0; |
|
145 | border: 0; | |
146 | outline: 0; |
|
146 | outline: 0; | |
147 | font-size: @basefontsize*.95; |
|
147 | font-size: @basefontsize*.95; | |
148 | line-height: 1.4em; |
|
148 | line-height: 1.4em; | |
149 | font-family: @text-code; |
|
149 | font-family: @text-code; | |
150 | color: @grey3; |
|
150 | color: @grey3; | |
151 | } |
|
151 | } | |
152 |
|
152 | |||
153 | // Emphasis & misc |
|
153 | // Emphasis & misc | |
154 | // ------------------------- |
|
154 | // ------------------------- | |
155 |
|
155 | |||
156 | small, |
|
156 | small, | |
157 | .small { |
|
157 | .small { | |
158 | font-size: 75%; |
|
158 | font-size: 75%; | |
159 | font-weight: normal; |
|
159 | font-weight: normal; | |
160 | line-height: 1em; |
|
160 | line-height: 1em; | |
161 | } |
|
161 | } | |
162 |
|
162 | |||
163 | mark, |
|
163 | mark, | |
164 | .mark { |
|
164 | .mark { | |
165 | background-color: @rclightblue; |
|
165 | background-color: @rclightblue; | |
166 | padding: .2em; |
|
166 | padding: .2em; | |
167 | } |
|
167 | } | |
168 |
|
168 | |||
169 | // Alignment |
|
169 | // Alignment | |
170 | .text-left { text-align: left; } |
|
170 | .text-left { text-align: left; } | |
171 | .text-right { text-align: right; } |
|
171 | .text-right { text-align: right; } | |
172 | .text-center { text-align: center; } |
|
172 | .text-center { text-align: center; } | |
173 | .text-justify { text-align: justify; } |
|
173 | .text-justify { text-align: justify; } | |
174 | .text-nowrap { white-space: nowrap; } |
|
174 | .text-nowrap { white-space: nowrap; } | |
175 |
|
175 | |||
176 | // Transformation |
|
176 | // Transformation | |
177 | .text-lowercase { text-transform: lowercase; } |
|
177 | .text-lowercase { text-transform: lowercase; } | |
178 | .text-uppercase { text-transform: uppercase; } |
|
178 | .text-uppercase { text-transform: uppercase; } | |
179 | .text-capitalize { text-transform: capitalize; } |
|
179 | .text-capitalize { text-transform: capitalize; } | |
180 |
|
180 | |||
181 | // Contextual colors |
|
181 | // Contextual colors | |
182 | .text-muted { |
|
182 | .text-muted { | |
183 | color: @grey4; |
|
183 | color: @grey4; | |
184 | } |
|
184 | } | |
185 | .text-primary { |
|
185 | .text-primary { | |
186 | color: @rcblue; |
|
186 | color: @rcblue; | |
187 | } |
|
187 | } | |
188 | .text-success { |
|
188 | .text-success { | |
189 | color: @alert1; |
|
189 | color: @alert1; | |
190 | } |
|
190 | } | |
191 | .text-info { |
|
191 | .text-info { | |
192 | color: @alert4; |
|
192 | color: @alert4; | |
193 | } |
|
193 | } | |
194 | .text-warning { |
|
194 | .text-warning { | |
195 | color: @alert3; |
|
195 | color: @alert3; | |
196 | } |
|
196 | } | |
197 | .text-danger { |
|
197 | .text-danger { | |
198 | color: @alert2; |
|
198 | color: @alert2; | |
199 | } |
|
199 | } | |
200 |
|
200 | |||
201 | // Contextual backgrounds |
|
201 | // Contextual backgrounds | |
202 | .bg-primary { |
|
202 | .bg-primary { | |
203 | background-color: white; |
|
203 | background-color: white; | |
204 | } |
|
204 | } | |
205 | .bg-success { |
|
205 | .bg-success { | |
206 | background-color: @alert1; |
|
206 | background-color: @alert1; | |
207 | } |
|
207 | } | |
208 | .bg-info { |
|
208 | .bg-info { | |
209 | background-color: @alert4; |
|
209 | background-color: @alert4; | |
210 | } |
|
210 | } | |
211 | .bg-warning { |
|
211 | .bg-warning { | |
212 | background-color: @alert3; |
|
212 | background-color: @alert3; | |
213 | } |
|
213 | } | |
214 | .bg-danger { |
|
214 | .bg-danger { | |
215 | background-color: @alert2; |
|
215 | background-color: @alert2; | |
216 | } |
|
216 | } | |
217 |
|
217 | |||
218 |
|
218 | |||
219 | // Page header |
|
219 | // Page header | |
220 | // ------------------------- |
|
220 | // ------------------------- | |
221 |
|
221 | |||
222 | .page-header { |
|
222 | .page-header { | |
223 | margin: @pagepadding 0 @textmargin; |
|
223 | margin: @pagepadding 0 @textmargin; | |
224 | border-bottom: @border-thickness solid @grey5; |
|
224 | border-bottom: @border-thickness solid @grey5; | |
225 | } |
|
225 | } | |
226 |
|
226 | |||
227 | .title { |
|
227 | .title { | |
228 | clear: both; |
|
228 | clear: both; | |
229 | float: left; |
|
229 | float: left; | |
230 | width: 100%; |
|
230 | width: 100%; | |
231 | margin: @pagepadding 0 @pagepadding; |
|
231 | margin: @pagepadding/2 0 @pagepadding; | |
232 |
|
232 | |||
233 | .breadcrumbs{ |
|
233 | .breadcrumbs{ | |
234 | float: left; |
|
234 | float: left; | |
235 | clear: both; |
|
235 | clear: both; | |
236 | width: 700px; |
|
236 | width: 700px; | |
237 | margin: 0; |
|
237 | margin: 0; | |
238 |
|
238 | |||
239 | .q_filter_box { |
|
239 | .q_filter_box { | |
240 | margin-right: @padding; |
|
240 | margin-right: @padding; | |
241 | } |
|
241 | } | |
242 | } |
|
242 | } | |
243 |
|
243 | |||
244 | h1 a { |
|
244 | h1 a { | |
245 | color: @rcblue; |
|
245 | color: @rcblue; | |
246 | } |
|
246 | } | |
247 |
|
247 | |||
248 | input{ |
|
248 | input{ | |
249 | margin-right: @padding; |
|
249 | margin-right: @padding; | |
250 | } |
|
250 | } | |
251 |
|
251 | |||
252 | h5, .h5 { |
|
252 | h5, .h5 { | |
253 | color: @grey1; |
|
253 | color: @grey1; | |
254 | margin-bottom: @space; |
|
254 | margin-bottom: @space; | |
255 |
|
255 | |||
256 | span { |
|
256 | span { | |
257 | display: inline-block; |
|
257 | display: inline-block; | |
258 | } |
|
258 | } | |
259 | } |
|
259 | } | |
260 |
|
260 | |||
261 | p { |
|
261 | p { | |
262 | margin-bottom: 0; |
|
262 | margin-bottom: 0; | |
263 | } |
|
263 | } | |
264 |
|
264 | |||
265 | .links { |
|
265 | .links { | |
266 | float: right; |
|
266 | float: right; | |
267 | display: inline; |
|
267 | display: inline; | |
268 | margin: 0; |
|
268 | margin: 0; | |
269 | padding-left: 0; |
|
269 | padding-left: 0; | |
270 | list-style: none; |
|
270 | list-style: none; | |
271 | text-align: right; |
|
271 | text-align: right; | |
272 |
|
272 | |||
273 | li:before { content: none; } |
|
273 | li:before { content: none; } | |
274 | li { float: right; } |
|
274 | li { float: right; } | |
275 | a { |
|
275 | a { | |
276 | display: inline-block; |
|
276 | display: inline-block; | |
277 | margin-left: @textmargin/2; |
|
277 | margin-left: @textmargin/2; | |
278 | } |
|
278 | } | |
279 | } |
|
279 | } | |
280 |
|
280 | |||
281 | .title-content { |
|
281 | .title-content { | |
282 | float: left; |
|
282 | float: left; | |
283 | margin: 0; |
|
283 | margin: 0; | |
284 | padding: 0; |
|
284 | padding: 0; | |
285 |
|
285 | |||
286 | & + .breadcrumbs { |
|
286 | & + .breadcrumbs { | |
287 | margin-top: @padding; |
|
287 | margin-top: @padding; | |
288 | } |
|
288 | } | |
289 |
|
289 | |||
290 | & + .links { |
|
290 | & + .links { | |
291 | margin-top: -@button-padding; |
|
291 | margin-top: -@button-padding; | |
292 |
|
292 | |||
293 | & + .breadcrumbs { |
|
293 | & + .breadcrumbs { | |
294 | margin-top: @padding; |
|
294 | margin-top: @padding; | |
295 | } |
|
295 | } | |
296 | } |
|
296 | } | |
297 | } |
|
297 | } | |
298 |
|
298 | |||
299 | .title-main { |
|
299 | .title-main { | |
300 | font-size: @repo-title-fontsize; |
|
300 | font-size: @repo-title-fontsize; | |
301 | } |
|
301 | } | |
302 |
|
302 | |||
303 | .title-description { |
|
303 | .title-description { | |
304 | margin-top: .5em; |
|
304 | margin-top: .5em; | |
305 | } |
|
305 | } | |
306 |
|
306 | |||
307 | .q_filter_box { |
|
307 | .q_filter_box { | |
308 | width: 200px; |
|
308 | width: 200px; | |
309 | } |
|
309 | } | |
310 |
|
310 | |||
311 | } |
|
311 | } | |
312 |
|
312 | |||
313 | #readme .title { |
|
313 | #readme .title { | |
314 | text-transform: none; |
|
314 | text-transform: none; | |
315 | } |
|
315 | } | |
316 |
|
316 | |||
317 | // Lists |
|
317 | // Lists | |
318 | // ------------------------- |
|
318 | // ------------------------- | |
319 |
|
319 | |||
320 | // Unordered and Ordered lists |
|
320 | // Unordered and Ordered lists | |
321 | ul, |
|
321 | ul, | |
322 | ol { |
|
322 | ol { | |
323 | margin-top: 0; |
|
323 | margin-top: 0; | |
324 | margin-bottom: @textmargin; |
|
324 | margin-bottom: @textmargin; | |
325 | ul, |
|
325 | ul, | |
326 | ol { |
|
326 | ol { | |
327 | margin-bottom: 0; |
|
327 | margin-bottom: 0; | |
328 | } |
|
328 | } | |
329 | } |
|
329 | } | |
330 |
|
330 | |||
331 | li { |
|
331 | li { | |
332 | line-height: 2em; |
|
332 | line-height: 2em; | |
333 | } |
|
333 | } | |
334 |
|
334 | |||
335 | ul li { |
|
335 | ul li { | |
336 | position: relative; |
|
336 | position: relative; | |
337 | display: block; |
|
337 | display: block; | |
338 | list-style-type: none; |
|
338 | list-style-type: none; | |
339 |
|
339 | |||
340 | &:before { |
|
340 | &:before { | |
341 | content: "\2014\00A0"; |
|
341 | content: "\2014\00A0"; | |
342 | position: absolute; |
|
342 | position: absolute; | |
343 | top: 0; |
|
343 | top: 0; | |
344 | left: -1.25em; |
|
344 | left: -1.25em; | |
345 | } |
|
345 | } | |
346 |
|
346 | |||
347 | p:first-child { |
|
347 | p:first-child { | |
348 | display:inline; |
|
348 | display:inline; | |
349 | } |
|
349 | } | |
350 | } |
|
350 | } | |
351 |
|
351 | |||
352 | // List options |
|
352 | // List options | |
353 |
|
353 | |||
354 | // Unstyled keeps list items block level, just removes default browser padding and list-style |
|
354 | // Unstyled keeps list items block level, just removes default browser padding and list-style | |
355 | .list-unstyled { |
|
355 | .list-unstyled { | |
356 | padding-left: 0; |
|
356 | padding-left: 0; | |
357 | list-style: none; |
|
357 | list-style: none; | |
358 | li:before { content: none; } |
|
358 | li:before { content: none; } | |
359 | } |
|
359 | } | |
360 |
|
360 | |||
361 | // Inline turns list items into inline-block |
|
361 | // Inline turns list items into inline-block | |
362 | .list-inline { |
|
362 | .list-inline { | |
363 | .list-unstyled(); |
|
363 | .list-unstyled(); | |
364 | margin-left: -5px; |
|
364 | margin-left: -5px; | |
365 |
|
365 | |||
366 | > li { |
|
366 | > li { | |
367 | display: inline-block; |
|
367 | display: inline-block; | |
368 | padding-left: 5px; |
|
368 | padding-left: 5px; | |
369 | padding-right: 5px; |
|
369 | padding-right: 5px; | |
370 | } |
|
370 | } | |
371 | } |
|
371 | } | |
372 |
|
372 | |||
373 | // Description Lists |
|
373 | // Description Lists | |
374 |
|
374 | |||
375 | dl { |
|
375 | dl { | |
376 | margin-top: 0; // Remove browser default |
|
376 | margin-top: 0; // Remove browser default | |
377 | margin-bottom: @textmargin; |
|
377 | margin-bottom: @textmargin; | |
378 | } |
|
378 | } | |
379 |
|
379 | |||
380 | dt, |
|
380 | dt, | |
381 | dd { |
|
381 | dd { | |
382 | line-height: 1.4em; |
|
382 | line-height: 1.4em; | |
383 | } |
|
383 | } | |
384 |
|
384 | |||
385 | dt { |
|
385 | dt { | |
386 | margin: @textmargin 0 0 0; |
|
386 | margin: @textmargin 0 0 0; | |
387 | font-family: @text-bold; |
|
387 | font-family: @text-bold; | |
388 | } |
|
388 | } | |
389 |
|
389 | |||
390 | dd { |
|
390 | dd { | |
391 | margin-left: 0; // Undo browser default |
|
391 | margin-left: 0; // Undo browser default | |
392 | } |
|
392 | } | |
393 |
|
393 | |||
394 | // Horizontal description lists |
|
394 | // Horizontal description lists | |
395 | // Defaults to being stacked without any of the below styles applied, until the |
|
395 | // Defaults to being stacked without any of the below styles applied, until the | |
396 | // grid breakpoint is reached (default of ~768px). |
|
396 | // grid breakpoint is reached (default of ~768px). | |
397 | // These are used in forms as well; see style guide. |
|
397 | // These are used in forms as well; see style guide. | |
398 | // TODO: lisa: These should really not be used in forms. |
|
398 | // TODO: lisa: These should really not be used in forms. | |
399 |
|
399 | |||
400 | .dl-horizontal { |
|
400 | .dl-horizontal { | |
401 |
|
401 | |||
402 | overflow: hidden; |
|
402 | overflow: hidden; | |
403 | margin-bottom: @space; |
|
403 | margin-bottom: @space; | |
404 |
|
404 | |||
405 | dt, dd { |
|
405 | dt, dd { | |
406 | float: left; |
|
406 | float: left; | |
407 | margin: 5px 0 5px 0; |
|
407 | margin: 5px 0 5px 0; | |
408 | } |
|
408 | } | |
409 |
|
409 | |||
410 | dt { |
|
410 | dt { | |
411 | clear: left; |
|
411 | clear: left; | |
412 | width: @label-width - @form-vertical-margin; |
|
412 | width: @label-width - @form-vertical-margin; | |
413 | } |
|
413 | } | |
414 |
|
414 | |||
415 | dd { |
|
415 | dd { | |
416 | &:extend(.clearfix all); // Clear the floated `dt` if an empty `dd` is present |
|
416 | &:extend(.clearfix all); // Clear the floated `dt` if an empty `dd` is present | |
417 | margin-left: @form-vertical-margin; |
|
417 | margin-left: @form-vertical-margin; | |
418 | max-width: @form-max-width - (@label-width - @form-vertical-margin) - @form-vertical-margin; |
|
418 | max-width: @form-max-width - (@label-width - @form-vertical-margin) - @form-vertical-margin; | |
419 | } |
|
419 | } | |
420 |
|
420 | |||
421 | pre { |
|
421 | pre { | |
422 | margin: 0; |
|
422 | margin: 0; | |
423 | } |
|
423 | } | |
424 |
|
424 | |||
425 | &.settings { |
|
425 | &.settings { | |
426 | dt { |
|
426 | dt { | |
427 | text-align: left; |
|
427 | text-align: left; | |
428 | } |
|
428 | } | |
429 | } |
|
429 | } | |
430 |
|
430 | |||
431 | @media (min-width: 768px) { |
|
431 | @media (min-width: 768px) { | |
432 | dt { |
|
432 | dt { | |
433 | float: left; |
|
433 | float: left; | |
434 | width: 180px; |
|
434 | width: 180px; | |
435 | clear: left; |
|
435 | clear: left; | |
436 | text-align: right; |
|
436 | text-align: right; | |
437 | } |
|
437 | } | |
438 | dd { |
|
438 | dd { | |
439 | margin-left: 20px; |
|
439 | margin-left: 20px; | |
440 | } |
|
440 | } | |
441 | } |
|
441 | } | |
442 | } |
|
442 | } | |
443 |
|
443 | |||
444 |
|
444 | |||
445 | // Misc |
|
445 | // Misc | |
446 | // ------------------------- |
|
446 | // ------------------------- | |
447 |
|
447 | |||
448 | // Abbreviations and acronyms |
|
448 | // Abbreviations and acronyms | |
449 | abbr[title], |
|
449 | abbr[title], | |
450 | abbr[data-original-title] { |
|
450 | abbr[data-original-title] { | |
451 | cursor: help; |
|
451 | cursor: help; | |
452 | border-bottom: @border-thickness dotted @grey4; |
|
452 | border-bottom: @border-thickness dotted @grey4; | |
453 | } |
|
453 | } | |
454 | .initialism { |
|
454 | .initialism { | |
455 | font-size: 90%; |
|
455 | font-size: 90%; | |
456 | text-transform: uppercase; |
|
456 | text-transform: uppercase; | |
457 | } |
|
457 | } | |
458 |
|
458 | |||
459 | // Blockquotes |
|
459 | // Blockquotes | |
460 | blockquote { |
|
460 | blockquote { | |
461 | padding: 1em 2em; |
|
461 | padding: 1em 2em; | |
462 | margin: 0 0 2em; |
|
462 | margin: 0 0 2em; | |
463 | font-size: @basefontsize; |
|
463 | font-size: @basefontsize; | |
464 | border-left: 2px solid @grey6; |
|
464 | border-left: 2px solid @grey6; | |
465 |
|
465 | |||
466 | p, |
|
466 | p, | |
467 | ul, |
|
467 | ul, | |
468 | ol { |
|
468 | ol { | |
469 | &:last-child { |
|
469 | &:last-child { | |
470 | margin-bottom: 0; |
|
470 | margin-bottom: 0; | |
471 | } |
|
471 | } | |
472 | } |
|
472 | } | |
473 |
|
473 | |||
474 | footer, |
|
474 | footer, | |
475 | small, |
|
475 | small, | |
476 | .small { |
|
476 | .small { | |
477 | display: block; |
|
477 | display: block; | |
478 | font-size: 80%; |
|
478 | font-size: 80%; | |
479 |
|
479 | |||
480 | &:before { |
|
480 | &:before { | |
481 | content: '\2014 \00A0'; // em dash, nbsp |
|
481 | content: '\2014 \00A0'; // em dash, nbsp | |
482 | } |
|
482 | } | |
483 | } |
|
483 | } | |
484 | } |
|
484 | } | |
485 |
|
485 | |||
486 | // Opposite alignment of blockquote |
|
486 | // Opposite alignment of blockquote | |
487 | // |
|
487 | // | |
488 | .blockquote-reverse, |
|
488 | .blockquote-reverse, | |
489 | blockquote.pull-right { |
|
489 | blockquote.pull-right { | |
490 | padding-right: 15px; |
|
490 | padding-right: 15px; | |
491 | padding-left: 0; |
|
491 | padding-left: 0; | |
492 | border-right: 5px solid @grey6; |
|
492 | border-right: 5px solid @grey6; | |
493 | border-left: 0; |
|
493 | border-left: 0; | |
494 | text-align: right; |
|
494 | text-align: right; | |
495 |
|
495 | |||
496 | // Account for citation |
|
496 | // Account for citation | |
497 | footer, |
|
497 | footer, | |
498 | small, |
|
498 | small, | |
499 | .small { |
|
499 | .small { | |
500 | &:before { content: ''; } |
|
500 | &:before { content: ''; } | |
501 | &:after { |
|
501 | &:after { | |
502 | content: '\00A0 \2014'; // nbsp, em dash |
|
502 | content: '\00A0 \2014'; // nbsp, em dash | |
503 | } |
|
503 | } | |
504 | } |
|
504 | } | |
505 | } |
|
505 | } | |
506 |
|
506 | |||
507 | // Addresses |
|
507 | // Addresses | |
508 | address { |
|
508 | address { | |
509 | margin-bottom: 2em; |
|
509 | margin-bottom: 2em; | |
510 | font-style: normal; |
|
510 | font-style: normal; | |
511 | line-height: 1.8em; |
|
511 | line-height: 1.8em; | |
512 | } |
|
512 | } | |
513 |
|
513 | |||
514 | .error-message { |
|
514 | .error-message { | |
515 | display: block; |
|
515 | display: block; | |
516 | margin: @padding/3 0; |
|
516 | margin: @padding/3 0; | |
517 | color: @alert2; |
|
517 | color: @alert2; | |
518 | } |
|
518 | } | |
519 |
|
519 | |||
520 | .issue-tracker-link { |
|
520 | .issue-tracker-link { | |
521 | color: @rcblue; |
|
521 | color: @rcblue; | |
522 | } |
|
522 | } | |
523 |
|
523 | |||
524 | .info_text{ |
|
524 | .info_text{ | |
525 | font-size: @basefontsize; |
|
525 | font-size: @basefontsize; | |
526 | color: @grey4; |
|
526 | color: @grey4; | |
527 | font-family: @text-regular; |
|
527 | font-family: @text-regular; | |
528 | } |
|
528 | } | |
529 |
|
529 | |||
530 | // help block text |
|
530 | // help block text | |
531 | .help-block { |
|
531 | .help-block { | |
532 | display: block; |
|
532 | display: block; | |
533 | margin: 0 0 @padding; |
|
533 | margin: 0 0 @padding; | |
534 | color: @grey4; |
|
534 | color: @grey4; | |
535 | font-family: @text-light; |
|
535 | font-family: @text-light; | |
536 | &.pre-formatting { |
|
536 | &.pre-formatting { | |
537 | white-space: pre; |
|
537 | white-space: pre; | |
538 | } |
|
538 | } | |
539 | } |
|
539 | } | |
540 |
|
540 | |||
541 | .error-message { |
|
541 | .error-message { | |
542 | display: block; |
|
542 | display: block; | |
543 | margin: @padding/3 0; |
|
543 | margin: @padding/3 0; | |
544 | color: @alert2; |
|
544 | color: @alert2; | |
545 | } |
|
545 | } |
@@ -1,101 +1,101 b'' | |||||
1 | // Global keyboard bindings |
|
1 | // Global keyboard bindings | |
2 |
|
2 | |||
3 | function setRCMouseBindings(repoName, repoLandingRev) { |
|
3 | function setRCMouseBindings(repoName, repoLandingRev) { | |
4 |
|
4 | |||
5 | /** custom callback for supressing mousetrap from firing */ |
|
5 | /** custom callback for supressing mousetrap from firing */ | |
6 | Mousetrap.stopCallback = function(e, element) { |
|
6 | Mousetrap.stopCallback = function(e, element) { | |
7 | // if the element has the class "mousetrap" then no need to stop |
|
7 | // if the element has the class "mousetrap" then no need to stop | |
8 | if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) { |
|
8 | if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) { | |
9 | return false; |
|
9 | return false; | |
10 | } |
|
10 | } | |
11 |
|
11 | |||
12 | // stop for input, select, and textarea |
|
12 | // stop for input, select, and textarea | |
13 | return element.tagName == 'INPUT' || element.tagName == 'SELECT' || element.tagName == 'TEXTAREA' || element.isContentEditable; |
|
13 | return element.tagName == 'INPUT' || element.tagName == 'SELECT' || element.tagName == 'TEXTAREA' || element.isContentEditable; | |
14 | }; |
|
14 | }; | |
15 |
|
15 | |||
16 | // general help "?" |
|
16 | // general help "?" | |
17 | Mousetrap.bind(['?'], function(e) { |
|
17 | Mousetrap.bind(['?'], function(e) { | |
18 | $('#help_kb').modal({}); |
|
18 | $('#help_kb').modal({}); | |
19 | }); |
|
19 | }); | |
20 |
|
20 | |||
21 | // / open the quick filter |
|
21 | // / open the quick filter | |
22 | Mousetrap.bind(['/'], function(e) { |
|
22 | Mousetrap.bind(['/'], function(e) { | |
23 | $('#repo_switcher').select2('open'); |
|
23 | $('#main_filter').get(0).focus(); | |
24 |
|
24 | |||
25 | // return false to prevent default browser behavior |
|
25 | // return false to prevent default browser behavior | |
26 | // and stop event from bubbling |
|
26 | // and stop event from bubbling | |
27 | return false; |
|
27 | return false; | |
28 | }); |
|
28 | }); | |
29 |
|
29 | |||
30 | // ctrl/command+b, show the the main bar |
|
30 | // ctrl/command+b, show the the main bar | |
31 | Mousetrap.bind(['command+b', 'ctrl+b'], function(e) { |
|
31 | Mousetrap.bind(['command+b', 'ctrl+b'], function(e) { | |
32 | var $headerInner = $('#header-inner'), |
|
32 | var $headerInner = $('#header-inner'), | |
33 | $content = $('#content'); |
|
33 | $content = $('#content'); | |
34 | if ($headerInner.hasClass('hover') && $content.hasClass('hover')) { |
|
34 | if ($headerInner.hasClass('hover') && $content.hasClass('hover')) { | |
35 | $headerInner.removeClass('hover'); |
|
35 | $headerInner.removeClass('hover'); | |
36 | $content.removeClass('hover'); |
|
36 | $content.removeClass('hover'); | |
37 | } else { |
|
37 | } else { | |
38 | $headerInner.addClass('hover'); |
|
38 | $headerInner.addClass('hover'); | |
39 | $content.addClass('hover'); |
|
39 | $content.addClass('hover'); | |
40 | } |
|
40 | } | |
41 | return false; |
|
41 | return false; | |
42 | }); |
|
42 | }); | |
43 |
|
43 | |||
44 | // general nav g + action |
|
44 | // general nav g + action | |
45 | Mousetrap.bind(['g h'], function(e) { |
|
45 | Mousetrap.bind(['g h'], function(e) { | |
46 | window.location = pyroutes.url('home'); |
|
46 | window.location = pyroutes.url('home'); | |
47 | }); |
|
47 | }); | |
48 | Mousetrap.bind(['g g'], function(e) { |
|
48 | Mousetrap.bind(['g g'], function(e) { | |
49 | window.location = pyroutes.url('gists_show', {'private': 1}); |
|
49 | window.location = pyroutes.url('gists_show', {'private': 1}); | |
50 | }); |
|
50 | }); | |
51 | Mousetrap.bind(['g G'], function(e) { |
|
51 | Mousetrap.bind(['g G'], function(e) { | |
52 | window.location = pyroutes.url('gists_show', {'public': 1}); |
|
52 | window.location = pyroutes.url('gists_show', {'public': 1}); | |
53 | }); |
|
53 | }); | |
54 | Mousetrap.bind(['n g'], function(e) { |
|
54 | Mousetrap.bind(['n g'], function(e) { | |
55 | window.location = pyroutes.url('gists_new'); |
|
55 | window.location = pyroutes.url('gists_new'); | |
56 | }); |
|
56 | }); | |
57 | Mousetrap.bind(['n r'], function(e) { |
|
57 | Mousetrap.bind(['n r'], function(e) { | |
58 | window.location = pyroutes.url('repo_new'); |
|
58 | window.location = pyroutes.url('repo_new'); | |
59 | }); |
|
59 | }); | |
60 |
|
60 | |||
61 | if (repoName && repoName != '') { |
|
61 | if (repoName && repoName != '') { | |
62 | // nav in repo context |
|
62 | // nav in repo context | |
63 | Mousetrap.bind(['g s'], function(e) { |
|
63 | Mousetrap.bind(['g s'], function(e) { | |
64 | window.location = pyroutes.url( |
|
64 | window.location = pyroutes.url( | |
65 | 'repo_summary', {'repo_name': repoName}); |
|
65 | 'repo_summary', {'repo_name': repoName}); | |
66 | }); |
|
66 | }); | |
67 | Mousetrap.bind(['g c'], function(e) { |
|
67 | Mousetrap.bind(['g c'], function(e) { | |
68 | window.location = pyroutes.url( |
|
68 | window.location = pyroutes.url( | |
69 | 'repo_changelog', {'repo_name': repoName}); |
|
69 | 'repo_changelog', {'repo_name': repoName}); | |
70 | }); |
|
70 | }); | |
71 | Mousetrap.bind(['g F'], function(e) { |
|
71 | Mousetrap.bind(['g F'], function(e) { | |
72 | window.location = pyroutes.url( |
|
72 | window.location = pyroutes.url( | |
73 | 'repo_files', |
|
73 | 'repo_files', | |
74 | { |
|
74 | { | |
75 | 'repo_name': repoName, |
|
75 | 'repo_name': repoName, | |
76 | 'commit_id': repoLandingRev, |
|
76 | 'commit_id': repoLandingRev, | |
77 | 'f_path': '', |
|
77 | 'f_path': '', | |
78 | 'search': '1' |
|
78 | 'search': '1' | |
79 | }); |
|
79 | }); | |
80 | }); |
|
80 | }); | |
81 | Mousetrap.bind(['g f'], function(e) { |
|
81 | Mousetrap.bind(['g f'], function(e) { | |
82 | window.location = pyroutes.url( |
|
82 | window.location = pyroutes.url( | |
83 | 'repo_files', |
|
83 | 'repo_files', | |
84 | { |
|
84 | { | |
85 | 'repo_name': repoName, |
|
85 | 'repo_name': repoName, | |
86 | 'commit_id': repoLandingRev, |
|
86 | 'commit_id': repoLandingRev, | |
87 | 'f_path': '' |
|
87 | 'f_path': '' | |
88 | }); |
|
88 | }); | |
89 | }); |
|
89 | }); | |
90 | Mousetrap.bind(['g o'], function(e) { |
|
90 | Mousetrap.bind(['g o'], function(e) { | |
91 | window.location = pyroutes.url( |
|
91 | window.location = pyroutes.url( | |
92 | 'edit_repo', {'repo_name': repoName}); |
|
92 | 'edit_repo', {'repo_name': repoName}); | |
93 | }); |
|
93 | }); | |
94 | Mousetrap.bind(['g O'], function(e) { |
|
94 | Mousetrap.bind(['g O'], function(e) { | |
95 | window.location = pyroutes.url( |
|
95 | window.location = pyroutes.url( | |
96 | 'edit_repo_perms', {'repo_name': repoName}); |
|
96 | 'edit_repo_perms', {'repo_name': repoName}); | |
97 | }); |
|
97 | }); | |
98 | } |
|
98 | } | |
99 | } |
|
99 | } | |
100 |
|
100 | |||
101 | setRCMouseBindings(templateContext.repo_name, templateContext.repo_landing_commit); |
|
101 | setRCMouseBindings(templateContext.repo_name, templateContext.repo_landing_commit); |
@@ -1,188 +1,188 b'' | |||||
1 | <div class="panel panel-default"> |
|
1 | <div class="panel panel-default"> | |
2 | <div class="panel-heading"> |
|
2 | <div class="panel-heading"> | |
3 | <h3 class="panel-title">${_('Authentication Tokens')}</h3> |
|
3 | <h3 class="panel-title">${_('Authentication Tokens')}</h3> | |
4 | </div> |
|
4 | </div> | |
5 | <div class="panel-body"> |
|
5 | <div class="panel-body"> | |
6 | <div class="apikeys_wrap"> |
|
6 | <div class="apikeys_wrap"> | |
7 | <p> |
|
7 | <p> | |
8 | ${_('Each token can have a role. Token with a role can be used only in given context, ' |
|
8 | ${_('Each token can have a role. Token with a role can be used only in given context, ' | |
9 | 'e.g. VCS tokens can be used together with the authtoken auth plugin for git/hg/svn operations only.')} |
|
9 | 'e.g. VCS tokens can be used together with the authtoken auth plugin for git/hg/svn operations only.')} | |
10 | </p> |
|
10 | </p> | |
11 | <table class="rctable auth_tokens"> |
|
11 | <table class="rctable auth_tokens"> | |
12 | <tr> |
|
12 | <tr> | |
13 | <th>${_('Token')}</th> |
|
13 | <th>${_('Token')}</th> | |
14 | <th>${_('Scope')}</th> |
|
14 | <th>${_('Scope')}</th> | |
15 | <th>${_('Description')}</th> |
|
15 | <th>${_('Description')}</th> | |
16 | <th>${_('Role')}</th> |
|
16 | <th>${_('Role')}</th> | |
17 | <th>${_('Expiration')}</th> |
|
17 | <th>${_('Expiration')}</th> | |
18 | <th>${_('Action')}</th> |
|
18 | <th>${_('Action')}</th> | |
19 | </tr> |
|
19 | </tr> | |
20 | %if c.user_auth_tokens: |
|
20 | %if c.user_auth_tokens: | |
21 | %for auth_token in c.user_auth_tokens: |
|
21 | %for auth_token in c.user_auth_tokens: | |
22 | <tr class="${'expired' if auth_token.expired else ''}"> |
|
22 | <tr class="${'expired' if auth_token.expired else ''}"> | |
23 | <td class="truncate-wrap td-authtoken"> |
|
23 | <td class="truncate-wrap td-authtoken"> | |
24 | <div class="user_auth_tokens truncate autoexpand"> |
|
24 | <div class="user_auth_tokens truncate autoexpand"> | |
25 | <code>${auth_token.api_key}</code> |
|
25 | <code>${auth_token.api_key}</code> | |
26 | </div> |
|
26 | </div> | |
27 | </td> |
|
27 | </td> | |
28 | <td class="td">${auth_token.scope_humanized}</td> |
|
28 | <td class="td">${auth_token.scope_humanized}</td> | |
29 | <td class="td-wrap">${auth_token.description}</td> |
|
29 | <td class="td-wrap">${auth_token.description}</td> | |
30 | <td class="td-tags"> |
|
30 | <td class="td-tags"> | |
31 | <span class="tag disabled">${auth_token.role_humanized}</span> |
|
31 | <span class="tag disabled">${auth_token.role_humanized}</span> | |
32 | </td> |
|
32 | </td> | |
33 | <td class="td-exp"> |
|
33 | <td class="td-exp"> | |
34 | %if auth_token.expires == -1: |
|
34 | %if auth_token.expires == -1: | |
35 | ${_('never')} |
|
35 | ${_('never')} | |
36 | %else: |
|
36 | %else: | |
37 | %if auth_token.expired: |
|
37 | %if auth_token.expired: | |
38 | <span style="text-decoration: line-through">${h.age_component(h.time_to_utcdatetime(auth_token.expires))}</span> |
|
38 | <span style="text-decoration: line-through">${h.age_component(h.time_to_utcdatetime(auth_token.expires))}</span> | |
39 | %else: |
|
39 | %else: | |
40 | ${h.age_component(h.time_to_utcdatetime(auth_token.expires))} |
|
40 | ${h.age_component(h.time_to_utcdatetime(auth_token.expires))} | |
41 | %endif |
|
41 | %endif | |
42 | %endif |
|
42 | %endif | |
43 | </td> |
|
43 | </td> | |
44 | <td class="td-action"> |
|
44 | <td class="td-action"> | |
45 | ${h.secure_form(h.route_path('my_account_auth_tokens_delete'), request=request)} |
|
45 | ${h.secure_form(h.route_path('my_account_auth_tokens_delete'), request=request)} | |
46 | ${h.hidden('del_auth_token', auth_token.user_api_key_id)} |
|
46 | ${h.hidden('del_auth_token', auth_token.user_api_key_id)} | |
47 | <button class="btn btn-link btn-danger" type="submit" |
|
47 | <button class="btn btn-link btn-danger" type="submit" | |
48 | onclick="return confirm('${_('Confirm to remove this auth token: %s') % auth_token.token_obfuscated}');"> |
|
48 | onclick="return confirm('${_('Confirm to remove this auth token: %s') % auth_token.token_obfuscated}');"> | |
49 | ${_('Delete')} |
|
49 | ${_('Delete')} | |
50 | </button> |
|
50 | </button> | |
51 | ${h.end_form()} |
|
51 | ${h.end_form()} | |
52 | </td> |
|
52 | </td> | |
53 | </tr> |
|
53 | </tr> | |
54 | %endfor |
|
54 | %endfor | |
55 | %else: |
|
55 | %else: | |
56 | <tr><td><div class="ip">${_('No additional auth tokens specified')}</div></td></tr> |
|
56 | <tr><td><div class="ip">${_('No additional auth tokens specified')}</div></td></tr> | |
57 | %endif |
|
57 | %endif | |
58 | </table> |
|
58 | </table> | |
59 | </div> |
|
59 | </div> | |
60 |
|
60 | |||
61 | <div class="user_auth_tokens"> |
|
61 | <div class="user_auth_tokens"> | |
62 | ${h.secure_form(h.route_path('my_account_auth_tokens_add'), request=request)} |
|
62 | ${h.secure_form(h.route_path('my_account_auth_tokens_add'), request=request)} | |
63 | <div class="form form-vertical"> |
|
63 | <div class="form form-vertical"> | |
64 | <!-- fields --> |
|
64 | <!-- fields --> | |
65 | <div class="fields"> |
|
65 | <div class="fields"> | |
66 | <div class="field"> |
|
66 | <div class="field"> | |
67 | <div class="label"> |
|
67 | <div class="label"> | |
68 | <label for="new_email">${_('New authentication token')}:</label> |
|
68 | <label for="new_email">${_('New authentication token')}:</label> | |
69 | </div> |
|
69 | </div> | |
70 | <div class="input"> |
|
70 | <div class="input"> | |
71 | ${h.text('description', class_='medium', placeholder=_('Description'))} |
|
71 | ${h.text('description', class_='medium', placeholder=_('Description'))} | |
72 | ${h.hidden('lifetime')} |
|
72 | ${h.hidden('lifetime')} | |
73 | ${h.select('role', '', c.role_options)} |
|
73 | ${h.select('role', '', c.role_options)} | |
74 |
|
74 | |||
75 | % if c.allow_scoped_tokens: |
|
75 | % if c.allow_scoped_tokens: | |
76 | ${h.hidden('scope_repo_id')} |
|
76 | ${h.hidden('scope_repo_id')} | |
77 | % else: |
|
77 | % else: | |
78 | ${h.select('scope_repo_id_disabled', '', ['Scopes available in EE edition'], disabled='disabled')} |
|
78 | ${h.select('scope_repo_id_disabled', '', ['Scopes available in EE edition'], disabled='disabled')} | |
79 | % endif |
|
79 | % endif | |
80 | </div> |
|
80 | </div> | |
81 | <p class="help-block"> |
|
81 | <p class="help-block"> | |
82 | ${_('Repository scope works only with tokens with VCS type.')} |
|
82 | ${_('Repository scope works only with tokens with VCS type.')} | |
83 | </p> |
|
83 | </p> | |
84 | </div> |
|
84 | </div> | |
85 | <div class="buttons"> |
|
85 | <div class="buttons"> | |
86 | ${h.submit('save',_('Add'),class_="btn")} |
|
86 | ${h.submit('save',_('Add'),class_="btn")} | |
87 | ${h.reset('reset',_('Reset'),class_="btn")} |
|
87 | ${h.reset('reset',_('Reset'),class_="btn")} | |
88 | </div> |
|
88 | </div> | |
89 | </div> |
|
89 | </div> | |
90 | </div> |
|
90 | </div> | |
91 | ${h.end_form()} |
|
91 | ${h.end_form()} | |
92 | </div> |
|
92 | </div> | |
93 | </div> |
|
93 | </div> | |
94 | </div> |
|
94 | </div> | |
95 | <script> |
|
95 | <script> | |
96 | $(document).ready(function(){ |
|
96 | $(document).ready(function(){ | |
97 |
|
97 | |||
98 | var select2Options = { |
|
98 | var select2Options = { | |
99 | 'containerCssClass': "drop-menu", |
|
99 | 'containerCssClass': "drop-menu", | |
100 | 'dropdownCssClass': "drop-menu-dropdown", |
|
100 | 'dropdownCssClass': "drop-menu-dropdown", | |
101 | 'dropdownAutoWidth': true |
|
101 | 'dropdownAutoWidth': true | |
102 | }; |
|
102 | }; | |
103 | $("#role").select2(select2Options); |
|
103 | $("#role").select2(select2Options); | |
104 |
|
104 | |||
105 | var preloadData = { |
|
105 | var preloadData = { | |
106 | results: [ |
|
106 | results: [ | |
107 | % for entry in c.lifetime_values: |
|
107 | % for entry in c.lifetime_values: | |
108 | {id:${entry[0]}, text:"${entry[1]}"}${'' if loop.last else ','} |
|
108 | {id:${entry[0]}, text:"${entry[1]}"}${'' if loop.last else ','} | |
109 | % endfor |
|
109 | % endfor | |
110 | ] |
|
110 | ] | |
111 | }; |
|
111 | }; | |
112 |
|
112 | |||
113 | $("#lifetime").select2({ |
|
113 | $("#lifetime").select2({ | |
114 | containerCssClass: "drop-menu", |
|
114 | containerCssClass: "drop-menu", | |
115 | dropdownCssClass: "drop-menu-dropdown", |
|
115 | dropdownCssClass: "drop-menu-dropdown", | |
116 | dropdownAutoWidth: true, |
|
116 | dropdownAutoWidth: true, | |
117 | data: preloadData, |
|
117 | data: preloadData, | |
118 | placeholder: "${_('Select or enter expiration date')}", |
|
118 | placeholder: "${_('Select or enter expiration date')}", | |
119 | query: function(query) { |
|
119 | query: function(query) { | |
120 | feedLifetimeOptions(query, preloadData); |
|
120 | feedLifetimeOptions(query, preloadData); | |
121 | } |
|
121 | } | |
122 | }); |
|
122 | }); | |
123 |
|
123 | |||
124 |
|
124 | |||
125 | var repoFilter = function(data) { |
|
125 | var repoFilter = function(data) { | |
126 | var results = []; |
|
126 | var results = []; | |
127 |
|
127 | |||
128 | if (!data.results[0]) { |
|
128 | if (!data.results[0]) { | |
129 | return data |
|
129 | return data | |
130 | } |
|
130 | } | |
131 |
|
131 | |||
132 | $.each(data.results[0].children, function() { |
|
132 | $.each(data.results[0].children, function() { | |
133 | // replace name to ID for submision |
|
133 | // replace name to ID for submision | |
134 |
this.id = this. |
|
134 | this.id = this.repo_id; | |
135 | results.push(this); |
|
135 | results.push(this); | |
136 | }); |
|
136 | }); | |
137 |
|
137 | |||
138 | data.results[0].children = results; |
|
138 | data.results[0].children = results; | |
139 | return data; |
|
139 | return data; | |
140 | }; |
|
140 | }; | |
141 |
|
141 | |||
142 | $("#scope_repo_id_disabled").select2(select2Options); |
|
142 | $("#scope_repo_id_disabled").select2(select2Options); | |
143 |
|
143 | |||
144 | var selectVcsScope = function() { |
|
144 | var selectVcsScope = function() { | |
145 | // select vcs scope and disable input |
|
145 | // select vcs scope and disable input | |
146 | $("#role").select2("val", "${c.role_vcs}").trigger('change'); |
|
146 | $("#role").select2("val", "${c.role_vcs}").trigger('change'); | |
147 | $("#role").select2("readonly", true) |
|
147 | $("#role").select2("readonly", true) | |
148 | }; |
|
148 | }; | |
149 |
|
149 | |||
150 | $("#scope_repo_id").select2({ |
|
150 | $("#scope_repo_id").select2({ | |
151 | cachedDataSource: {}, |
|
151 | cachedDataSource: {}, | |
152 | minimumInputLength: 2, |
|
152 | minimumInputLength: 2, | |
153 | placeholder: "${_('repository scope')}", |
|
153 | placeholder: "${_('repository scope')}", | |
154 | dropdownAutoWidth: true, |
|
154 | dropdownAutoWidth: true, | |
155 | containerCssClass: "drop-menu", |
|
155 | containerCssClass: "drop-menu", | |
156 | dropdownCssClass: "drop-menu-dropdown", |
|
156 | dropdownCssClass: "drop-menu-dropdown", | |
157 | formatResult: formatResult, |
|
157 | formatResult: formatRepoResult, | |
158 | query: $.debounce(250, function(query){ |
|
158 | query: $.debounce(250, function(query){ | |
159 | self = this; |
|
159 | self = this; | |
160 | var cacheKey = query.term; |
|
160 | var cacheKey = query.term; | |
161 | var cachedData = self.cachedDataSource[cacheKey]; |
|
161 | var cachedData = self.cachedDataSource[cacheKey]; | |
162 |
|
162 | |||
163 | if (cachedData) { |
|
163 | if (cachedData) { | |
164 | query.callback({results: cachedData.results}); |
|
164 | query.callback({results: cachedData.results}); | |
165 | } else { |
|
165 | } else { | |
166 | $.ajax({ |
|
166 | $.ajax({ | |
167 | url: pyroutes.url('repo_list_data'), |
|
167 | url: pyroutes.url('repo_list_data'), | |
168 | data: {'query': query.term}, |
|
168 | data: {'query': query.term}, | |
169 | dataType: 'json', |
|
169 | dataType: 'json', | |
170 | type: 'GET', |
|
170 | type: 'GET', | |
171 | success: function(data) { |
|
171 | success: function(data) { | |
172 | data = repoFilter(data); |
|
172 | data = repoFilter(data); | |
173 | self.cachedDataSource[cacheKey] = data; |
|
173 | self.cachedDataSource[cacheKey] = data; | |
174 | query.callback({results: data.results}); |
|
174 | query.callback({results: data.results}); | |
175 | }, |
|
175 | }, | |
176 | error: function(data, textStatus, errorThrown) { |
|
176 | error: function(data, textStatus, errorThrown) { | |
177 | alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText)); |
|
177 | alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText)); | |
178 | } |
|
178 | } | |
179 | }) |
|
179 | }) | |
180 | } |
|
180 | } | |
181 | }) |
|
181 | }) | |
182 | }); |
|
182 | }); | |
183 | $("#scope_repo_id").on('select2-selecting', function(e){ |
|
183 | $("#scope_repo_id").on('select2-selecting', function(e){ | |
184 | selectVcsScope() |
|
184 | selectVcsScope() | |
185 | }); |
|
185 | }); | |
186 |
|
186 | |||
187 | }); |
|
187 | }); | |
188 | </script> |
|
188 | </script> |
@@ -1,211 +1,211 b'' | |||||
1 | <%namespace name="base" file="/base/base.mako"/> |
|
1 | <%namespace name="base" file="/base/base.mako"/> | |
2 |
|
2 | |||
3 | <% |
|
3 | <% | |
4 | elems = [ |
|
4 | elems = [ | |
5 | (_('Owner'), lambda:base.gravatar_with_user(c.rhodecode_db_repo.user.email), '', ''), |
|
5 | (_('Owner'), lambda:base.gravatar_with_user(c.rhodecode_db_repo.user.email), '', ''), | |
6 | (_('Created on'), h.format_date(c.rhodecode_db_repo.created_on), '', ''), |
|
6 | (_('Created on'), h.format_date(c.rhodecode_db_repo.created_on), '', ''), | |
7 | (_('Updated on'), h.format_date(c.rhodecode_db_repo.updated_on), '', ''), |
|
7 | (_('Updated on'), h.format_date(c.rhodecode_db_repo.updated_on), '', ''), | |
8 | (_('Cached Commit id'), lambda: h.link_to(c.rhodecode_db_repo.changeset_cache.get('short_id'), h.route_path('repo_commit',repo_name=c.repo_name,commit_id=c.rhodecode_db_repo.changeset_cache.get('raw_id'))), '', ''), |
|
8 | (_('Cached Commit id'), lambda: h.link_to(c.rhodecode_db_repo.changeset_cache.get('short_id'), h.route_path('repo_commit',repo_name=c.repo_name,commit_id=c.rhodecode_db_repo.changeset_cache.get('raw_id'))), '', ''), | |
9 | (_('Attached scoped tokens'), len(c.rhodecode_db_repo.scoped_tokens), '', [x.user for x in c.rhodecode_db_repo.scoped_tokens]), |
|
9 | (_('Attached scoped tokens'), len(c.rhodecode_db_repo.scoped_tokens), '', [x.user for x in c.rhodecode_db_repo.scoped_tokens]), | |
10 | ] |
|
10 | ] | |
11 | %> |
|
11 | %> | |
12 |
|
12 | |||
13 | <div class="panel panel-default"> |
|
13 | <div class="panel panel-default"> | |
14 | <div class="panel-heading" id="advanced-info" > |
|
14 | <div class="panel-heading" id="advanced-info" > | |
15 | <h3 class="panel-title">${_('Repository: %s') % c.rhodecode_db_repo.repo_name} <a class="permalink" href="#advanced-info"> ΒΆ</a></h3> |
|
15 | <h3 class="panel-title">${_('Repository: %s') % c.rhodecode_db_repo.repo_name} <a class="permalink" href="#advanced-info"> ΒΆ</a></h3> | |
16 | </div> |
|
16 | </div> | |
17 | <div class="panel-body"> |
|
17 | <div class="panel-body"> | |
18 | ${base.dt_info_panel(elems)} |
|
18 | ${base.dt_info_panel(elems)} | |
19 | </div> |
|
19 | </div> | |
20 | </div> |
|
20 | </div> | |
21 |
|
21 | |||
22 |
|
22 | |||
23 | <div class="panel panel-default"> |
|
23 | <div class="panel panel-default"> | |
24 | <div class="panel-heading" id="advanced-fork"> |
|
24 | <div class="panel-heading" id="advanced-fork"> | |
25 | <h3 class="panel-title">${_('Fork Reference')} <a class="permalink" href="#advanced-fork"> ΒΆ</a></h3> |
|
25 | <h3 class="panel-title">${_('Fork Reference')} <a class="permalink" href="#advanced-fork"> ΒΆ</a></h3> | |
26 | </div> |
|
26 | </div> | |
27 | <div class="panel-body"> |
|
27 | <div class="panel-body"> | |
28 | ${h.secure_form(h.route_path('edit_repo_advanced_fork', repo_name=c.rhodecode_db_repo.repo_name), request=request)} |
|
28 | ${h.secure_form(h.route_path('edit_repo_advanced_fork', repo_name=c.rhodecode_db_repo.repo_name), request=request)} | |
29 |
|
29 | |||
30 | % if c.rhodecode_db_repo.fork: |
|
30 | % if c.rhodecode_db_repo.fork: | |
31 | <div class="panel-body-title-text">${h.literal(_('This repository is a fork of %(repo_link)s') % {'repo_link': h.link_to_if(c.has_origin_repo_read_perm,c.rhodecode_db_repo.fork.repo_name, h.route_path('repo_summary', repo_name=c.rhodecode_db_repo.fork.repo_name))})} |
|
31 | <div class="panel-body-title-text">${h.literal(_('This repository is a fork of %(repo_link)s') % {'repo_link': h.link_to_if(c.has_origin_repo_read_perm,c.rhodecode_db_repo.fork.repo_name, h.route_path('repo_summary', repo_name=c.rhodecode_db_repo.fork.repo_name))})} | |
32 | | <button class="btn btn-link btn-danger" type="submit">Remove fork reference</button></div> |
|
32 | | <button class="btn btn-link btn-danger" type="submit">Remove fork reference</button></div> | |
33 | % endif |
|
33 | % endif | |
34 |
|
34 | |||
35 | <div class="field"> |
|
35 | <div class="field"> | |
36 | ${h.hidden('id_fork_of')} |
|
36 | ${h.hidden('id_fork_of')} | |
37 | ${h.submit('set_as_fork_%s' % c.rhodecode_db_repo.repo_name,_('Set'),class_="btn btn-small",)} |
|
37 | ${h.submit('set_as_fork_%s' % c.rhodecode_db_repo.repo_name,_('Set'),class_="btn btn-small",)} | |
38 | </div> |
|
38 | </div> | |
39 | <div class="field"> |
|
39 | <div class="field"> | |
40 | <span class="help-block">${_('Manually set this repository as a fork of another from the list')}</span> |
|
40 | <span class="help-block">${_('Manually set this repository as a fork of another from the list')}</span> | |
41 | </div> |
|
41 | </div> | |
42 | ${h.end_form()} |
|
42 | ${h.end_form()} | |
43 | </div> |
|
43 | </div> | |
44 | </div> |
|
44 | </div> | |
45 |
|
45 | |||
46 |
|
46 | |||
47 | <div class="panel panel-default"> |
|
47 | <div class="panel panel-default"> | |
48 | <div class="panel-heading" id="advanced-journal"> |
|
48 | <div class="panel-heading" id="advanced-journal"> | |
49 | <h3 class="panel-title">${_('Public Journal Visibility')} <a class="permalink" href="#advanced-journal"> ΒΆ</a></h3> |
|
49 | <h3 class="panel-title">${_('Public Journal Visibility')} <a class="permalink" href="#advanced-journal"> ΒΆ</a></h3> | |
50 | </div> |
|
50 | </div> | |
51 | <div class="panel-body"> |
|
51 | <div class="panel-body"> | |
52 | ${h.secure_form(h.route_path('edit_repo_advanced_journal', repo_name=c.rhodecode_db_repo.repo_name), request=request)} |
|
52 | ${h.secure_form(h.route_path('edit_repo_advanced_journal', repo_name=c.rhodecode_db_repo.repo_name), request=request)} | |
53 | <div class="field"> |
|
53 | <div class="field"> | |
54 | %if c.in_public_journal: |
|
54 | %if c.in_public_journal: | |
55 | <button class="btn btn-small" type="submit"> |
|
55 | <button class="btn btn-small" type="submit"> | |
56 | ${_('Remove from Public Journal')} |
|
56 | ${_('Remove from Public Journal')} | |
57 | </button> |
|
57 | </button> | |
58 | %else: |
|
58 | %else: | |
59 | <button class="btn btn-small" type="submit"> |
|
59 | <button class="btn btn-small" type="submit"> | |
60 | ${_('Add to Public Journal')} |
|
60 | ${_('Add to Public Journal')} | |
61 | </button> |
|
61 | </button> | |
62 | %endif |
|
62 | %endif | |
63 | </div> |
|
63 | </div> | |
64 | <div class="field" > |
|
64 | <div class="field" > | |
65 | <span class="help-block">${_('All actions made on this repository will be visible to everyone following the public journal.')}</span> |
|
65 | <span class="help-block">${_('All actions made on this repository will be visible to everyone following the public journal.')}</span> | |
66 | </div> |
|
66 | </div> | |
67 | ${h.end_form()} |
|
67 | ${h.end_form()} | |
68 | </div> |
|
68 | </div> | |
69 | </div> |
|
69 | </div> | |
70 |
|
70 | |||
71 |
|
71 | |||
72 | <div class="panel panel-default"> |
|
72 | <div class="panel panel-default"> | |
73 | <div class="panel-heading" id="advanced-locking"> |
|
73 | <div class="panel-heading" id="advanced-locking"> | |
74 | <h3 class="panel-title">${_('Locking state')} <a class="permalink" href="#advanced-locking"> ΒΆ</a></h3> |
|
74 | <h3 class="panel-title">${_('Locking state')} <a class="permalink" href="#advanced-locking"> ΒΆ</a></h3> | |
75 | </div> |
|
75 | </div> | |
76 | <div class="panel-body"> |
|
76 | <div class="panel-body"> | |
77 | ${h.secure_form(h.route_path('edit_repo_advanced_locking', repo_name=c.rhodecode_db_repo.repo_name), request=request)} |
|
77 | ${h.secure_form(h.route_path('edit_repo_advanced_locking', repo_name=c.rhodecode_db_repo.repo_name), request=request)} | |
78 |
|
78 | |||
79 | %if c.rhodecode_db_repo.locked[0]: |
|
79 | %if c.rhodecode_db_repo.locked[0]: | |
80 | <div class="panel-body-title-text">${'Locked by %s on %s. Lock reason: %s' % (h.person_by_id(c.rhodecode_db_repo.locked[0]), |
|
80 | <div class="panel-body-title-text">${'Locked by %s on %s. Lock reason: %s' % (h.person_by_id(c.rhodecode_db_repo.locked[0]), | |
81 | h.format_date(h. time_to_datetime(c.rhodecode_db_repo.locked[1])), c.rhodecode_db_repo.locked[2])}</div> |
|
81 | h.format_date(h. time_to_datetime(c.rhodecode_db_repo.locked[1])), c.rhodecode_db_repo.locked[2])}</div> | |
82 | %else: |
|
82 | %else: | |
83 | <div class="panel-body-title-text">${_('This Repository is not currently locked.')}</div> |
|
83 | <div class="panel-body-title-text">${_('This Repository is not currently locked.')}</div> | |
84 | %endif |
|
84 | %endif | |
85 |
|
85 | |||
86 | <div class="field" > |
|
86 | <div class="field" > | |
87 | %if c.rhodecode_db_repo.locked[0]: |
|
87 | %if c.rhodecode_db_repo.locked[0]: | |
88 | ${h.hidden('set_unlock', '1')} |
|
88 | ${h.hidden('set_unlock', '1')} | |
89 | <button class="btn btn-small" type="submit" |
|
89 | <button class="btn btn-small" type="submit" | |
90 | onclick="return confirm('${_('Confirm to unlock repository.')}');"> |
|
90 | onclick="return confirm('${_('Confirm to unlock repository.')}');"> | |
91 | <i class="icon-unlock"></i> |
|
91 | <i class="icon-unlock"></i> | |
92 | ${_('Unlock repository')} |
|
92 | ${_('Unlock repository')} | |
93 | </button> |
|
93 | </button> | |
94 | %else: |
|
94 | %else: | |
95 | ${h.hidden('set_lock', '1')} |
|
95 | ${h.hidden('set_lock', '1')} | |
96 | <button class="btn btn-small" type="submit" |
|
96 | <button class="btn btn-small" type="submit" | |
97 | onclick="return confirm('${_('Confirm to lock repository.')}');"> |
|
97 | onclick="return confirm('${_('Confirm to lock repository.')}');"> | |
98 | <i class="icon-lock"></i> |
|
98 | <i class="icon-lock"></i> | |
99 | ${_('Lock Repository')} |
|
99 | ${_('Lock Repository')} | |
100 | </button> |
|
100 | </button> | |
101 | %endif |
|
101 | %endif | |
102 | </div> |
|
102 | </div> | |
103 | <div class="field" > |
|
103 | <div class="field" > | |
104 | <span class="help-block"> |
|
104 | <span class="help-block"> | |
105 | ${_('Force repository locking. This only works when anonymous access is disabled. Pulling from the repository locks the repository to that user until the same user pushes to that repository again.')} |
|
105 | ${_('Force repository locking. This only works when anonymous access is disabled. Pulling from the repository locks the repository to that user until the same user pushes to that repository again.')} | |
106 | </span> |
|
106 | </span> | |
107 | </div> |
|
107 | </div> | |
108 | ${h.end_form()} |
|
108 | ${h.end_form()} | |
109 | </div> |
|
109 | </div> | |
110 | </div> |
|
110 | </div> | |
111 |
|
111 | |||
112 | <div class="panel panel-danger"> |
|
112 | <div class="panel panel-danger"> | |
113 | <div class="panel-heading" id="advanced-delete"> |
|
113 | <div class="panel-heading" id="advanced-delete"> | |
114 | <h3 class="panel-title">${_('Delete repository')} <a class="permalink" href="#advanced-delete"> ΒΆ</a></h3> |
|
114 | <h3 class="panel-title">${_('Delete repository')} <a class="permalink" href="#advanced-delete"> ΒΆ</a></h3> | |
115 | </div> |
|
115 | </div> | |
116 | <div class="panel-body"> |
|
116 | <div class="panel-body"> | |
117 | ${h.secure_form(h.route_path('edit_repo_advanced_delete', repo_name=c.repo_name), request=request)} |
|
117 | ${h.secure_form(h.route_path('edit_repo_advanced_delete', repo_name=c.repo_name), request=request)} | |
118 | <table class="display"> |
|
118 | <table class="display"> | |
119 | <tr> |
|
119 | <tr> | |
120 | <td> |
|
120 | <td> | |
121 | ${_ungettext('This repository has %s fork.', 'This repository has %s forks.', c.rhodecode_db_repo.forks.count()) % c.rhodecode_db_repo.forks.count()} |
|
121 | ${_ungettext('This repository has %s fork.', 'This repository has %s forks.', c.rhodecode_db_repo.forks.count()) % c.rhodecode_db_repo.forks.count()} | |
122 | </td> |
|
122 | </td> | |
123 | <td> |
|
123 | <td> | |
124 | %if c.rhodecode_db_repo.forks.count(): |
|
124 | %if c.rhodecode_db_repo.forks.count(): | |
125 | <input type="radio" name="forks" value="detach_forks" checked="checked"/> <label for="forks">${_('Detach forks')}</label> |
|
125 | <input type="radio" name="forks" value="detach_forks" checked="checked"/> <label for="forks">${_('Detach forks')}</label> | |
126 | %endif |
|
126 | %endif | |
127 | </td> |
|
127 | </td> | |
128 | <td> |
|
128 | <td> | |
129 | %if c.rhodecode_db_repo.forks.count(): |
|
129 | %if c.rhodecode_db_repo.forks.count(): | |
130 | <input type="radio" name="forks" value="delete_forks"/> <label for="forks">${_('Delete forks')}</label> |
|
130 | <input type="radio" name="forks" value="delete_forks"/> <label for="forks">${_('Delete forks')}</label> | |
131 | %endif |
|
131 | %endif | |
132 | </td> |
|
132 | </td> | |
133 | </tr> |
|
133 | </tr> | |
134 | </table> |
|
134 | </table> | |
135 | <div style="margin: 0 0 20px 0" class="fake-space"></div> |
|
135 | <div style="margin: 0 0 20px 0" class="fake-space"></div> | |
136 |
|
136 | |||
137 | <div class="field"> |
|
137 | <div class="field"> | |
138 | <button class="btn btn-small btn-danger" type="submit" |
|
138 | <button class="btn btn-small btn-danger" type="submit" | |
139 | onclick="return confirm('${_('Confirm to delete this repository: %s') % c.repo_name}');"> |
|
139 | onclick="return confirm('${_('Confirm to delete this repository: %s') % c.repo_name}');"> | |
140 | <i class="icon-remove-sign"></i> |
|
140 | <i class="icon-remove-sign"></i> | |
141 | ${_('Delete This Repository')} |
|
141 | ${_('Delete This Repository')} | |
142 | </button> |
|
142 | </button> | |
143 | </div> |
|
143 | </div> | |
144 | <div class="field"> |
|
144 | <div class="field"> | |
145 | <span class="help-block"> |
|
145 | <span class="help-block"> | |
146 | ${_('This repository will be renamed in a special way in order to make it inaccessible to RhodeCode Enterprise and its VCS systems. If you need to fully delete it from the file system, please do it manually, or with rhodecode-cleanup-repos command available in rhodecode-tools.')} |
|
146 | ${_('This repository will be renamed in a special way in order to make it inaccessible to RhodeCode Enterprise and its VCS systems. If you need to fully delete it from the file system, please do it manually, or with rhodecode-cleanup-repos command available in rhodecode-tools.')} | |
147 | </span> |
|
147 | </span> | |
148 | </div> |
|
148 | </div> | |
149 |
|
149 | |||
150 | ${h.end_form()} |
|
150 | ${h.end_form()} | |
151 | </div> |
|
151 | </div> | |
152 | </div> |
|
152 | </div> | |
153 |
|
153 | |||
154 |
|
154 | |||
155 | <script> |
|
155 | <script> | |
156 |
|
156 | |||
157 | var currentRepoId = ${c.rhodecode_db_repo.repo_id}; |
|
157 | var currentRepoId = ${c.rhodecode_db_repo.repo_id}; | |
158 |
|
158 | |||
159 | var repoTypeFilter = function(data) { |
|
159 | var repoTypeFilter = function(data) { | |
160 | var results = []; |
|
160 | var results = []; | |
161 |
|
161 | |||
162 | if (!data.results[0]) { |
|
162 | if (!data.results[0]) { | |
163 | return data |
|
163 | return data | |
164 | } |
|
164 | } | |
165 |
|
165 | |||
166 | $.each(data.results[0].children, function() { |
|
166 | $.each(data.results[0].children, function() { | |
167 | // filter out the SAME repo, it cannot be used as fork of itself |
|
167 | // filter out the SAME repo, it cannot be used as fork of itself | |
168 |
if (this |
|
168 | if (this.repo_id != currentRepoId) { | |
169 |
this.id = this. |
|
169 | this.id = this.repo_id; | |
170 | results.push(this) |
|
170 | results.push(this) | |
171 | } |
|
171 | } | |
172 | }); |
|
172 | }); | |
173 | data.results[0].children = results; |
|
173 | data.results[0].children = results; | |
174 | return data; |
|
174 | return data; | |
175 | }; |
|
175 | }; | |
176 |
|
176 | |||
177 | $("#id_fork_of").select2({ |
|
177 | $("#id_fork_of").select2({ | |
178 | cachedDataSource: {}, |
|
178 | cachedDataSource: {}, | |
179 | minimumInputLength: 2, |
|
179 | minimumInputLength: 2, | |
180 | placeholder: "${_('Change repository') if c.rhodecode_db_repo.fork else _('Pick repository')}", |
|
180 | placeholder: "${_('Change repository') if c.rhodecode_db_repo.fork else _('Pick repository')}", | |
181 | dropdownAutoWidth: true, |
|
181 | dropdownAutoWidth: true, | |
182 | containerCssClass: "drop-menu", |
|
182 | containerCssClass: "drop-menu", | |
183 | dropdownCssClass: "drop-menu-dropdown", |
|
183 | dropdownCssClass: "drop-menu-dropdown", | |
184 | formatResult: formatResult, |
|
184 | formatResult: formatRepoResult, | |
185 | query: $.debounce(250, function(query){ |
|
185 | query: $.debounce(250, function(query){ | |
186 | self = this; |
|
186 | self = this; | |
187 | var cacheKey = query.term; |
|
187 | var cacheKey = query.term; | |
188 | var cachedData = self.cachedDataSource[cacheKey]; |
|
188 | var cachedData = self.cachedDataSource[cacheKey]; | |
189 |
|
189 | |||
190 | if (cachedData) { |
|
190 | if (cachedData) { | |
191 | query.callback({results: cachedData.results}); |
|
191 | query.callback({results: cachedData.results}); | |
192 | } else { |
|
192 | } else { | |
193 | $.ajax({ |
|
193 | $.ajax({ | |
194 | url: pyroutes.url('repo_list_data'), |
|
194 | url: pyroutes.url('repo_list_data'), | |
195 | data: {'query': query.term, repo_type: '${c.rhodecode_db_repo.repo_type}'}, |
|
195 | data: {'query': query.term, repo_type: '${c.rhodecode_db_repo.repo_type}'}, | |
196 | dataType: 'json', |
|
196 | dataType: 'json', | |
197 | type: 'GET', |
|
197 | type: 'GET', | |
198 | success: function(data) { |
|
198 | success: function(data) { | |
199 | data = repoTypeFilter(data); |
|
199 | data = repoTypeFilter(data); | |
200 | self.cachedDataSource[cacheKey] = data; |
|
200 | self.cachedDataSource[cacheKey] = data; | |
201 | query.callback({results: data.results}); |
|
201 | query.callback({results: data.results}); | |
202 | }, |
|
202 | }, | |
203 | error: function(data, textStatus, errorThrown) { |
|
203 | error: function(data, textStatus, errorThrown) { | |
204 | alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText)); |
|
204 | alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText)); | |
205 | } |
|
205 | } | |
206 | }) |
|
206 | }) | |
207 | } |
|
207 | } | |
208 | }) |
|
208 | }) | |
209 | }); |
|
209 | }); | |
210 | </script> |
|
210 | </script> | |
211 |
|
211 |
@@ -1,186 +1,186 b'' | |||||
1 | <div class="panel panel-default"> |
|
1 | <div class="panel panel-default"> | |
2 | <div class="panel-heading"> |
|
2 | <div class="panel-heading"> | |
3 | <h3 class="panel-title">${_('Authentication Tokens')}</h3> |
|
3 | <h3 class="panel-title">${_('Authentication Tokens')}</h3> | |
4 | </div> |
|
4 | </div> | |
5 | <div class="panel-body"> |
|
5 | <div class="panel-body"> | |
6 | <div class="apikeys_wrap"> |
|
6 | <div class="apikeys_wrap"> | |
7 | <p> |
|
7 | <p> | |
8 | ${_('Each token can have a role. Token with a role can be used only in given context, ' |
|
8 | ${_('Each token can have a role. Token with a role can be used only in given context, ' | |
9 | 'e.g. VCS tokens can be used together with the authtoken auth plugin for git/hg/svn operations only.')} |
|
9 | 'e.g. VCS tokens can be used together with the authtoken auth plugin for git/hg/svn operations only.')} | |
10 | </p> |
|
10 | </p> | |
11 | <table class="rctable auth_tokens"> |
|
11 | <table class="rctable auth_tokens"> | |
12 | <tr> |
|
12 | <tr> | |
13 | <th>${_('Token')}</th> |
|
13 | <th>${_('Token')}</th> | |
14 | <th>${_('Scope')}</th> |
|
14 | <th>${_('Scope')}</th> | |
15 | <th>${_('Description')}</th> |
|
15 | <th>${_('Description')}</th> | |
16 | <th>${_('Role')}</th> |
|
16 | <th>${_('Role')}</th> | |
17 | <th>${_('Expiration')}</th> |
|
17 | <th>${_('Expiration')}</th> | |
18 | <th>${_('Action')}</th> |
|
18 | <th>${_('Action')}</th> | |
19 | </tr> |
|
19 | </tr> | |
20 | %if c.user_auth_tokens: |
|
20 | %if c.user_auth_tokens: | |
21 | %for auth_token in c.user_auth_tokens: |
|
21 | %for auth_token in c.user_auth_tokens: | |
22 | <tr class="${'expired' if auth_token.expired else ''}"> |
|
22 | <tr class="${'expired' if auth_token.expired else ''}"> | |
23 | <td class="truncate-wrap td-authtoken"><div class="user_auth_tokens truncate autoexpand"><code>${auth_token.api_key}</code></div></td> |
|
23 | <td class="truncate-wrap td-authtoken"><div class="user_auth_tokens truncate autoexpand"><code>${auth_token.api_key}</code></div></td> | |
24 | <td class="td">${auth_token.scope_humanized}</td> |
|
24 | <td class="td">${auth_token.scope_humanized}</td> | |
25 | <td class="td-wrap">${auth_token.description}</td> |
|
25 | <td class="td-wrap">${auth_token.description}</td> | |
26 | <td class="td-tags"> |
|
26 | <td class="td-tags"> | |
27 | <span class="tag disabled">${auth_token.role_humanized}</span> |
|
27 | <span class="tag disabled">${auth_token.role_humanized}</span> | |
28 | </td> |
|
28 | </td> | |
29 | <td class="td-exp"> |
|
29 | <td class="td-exp"> | |
30 | %if auth_token.expires == -1: |
|
30 | %if auth_token.expires == -1: | |
31 | ${_('never')} |
|
31 | ${_('never')} | |
32 | %else: |
|
32 | %else: | |
33 | %if auth_token.expired: |
|
33 | %if auth_token.expired: | |
34 | <span style="text-decoration: line-through">${h.age_component(h.time_to_utcdatetime(auth_token.expires))}</span> |
|
34 | <span style="text-decoration: line-through">${h.age_component(h.time_to_utcdatetime(auth_token.expires))}</span> | |
35 | %else: |
|
35 | %else: | |
36 | ${h.age_component(h.time_to_utcdatetime(auth_token.expires))} |
|
36 | ${h.age_component(h.time_to_utcdatetime(auth_token.expires))} | |
37 | %endif |
|
37 | %endif | |
38 | %endif |
|
38 | %endif | |
39 | </td> |
|
39 | </td> | |
40 | <td class="td-action"> |
|
40 | <td class="td-action"> | |
41 | ${h.secure_form(h.route_path('edit_user_auth_tokens_delete', user_id=c.user.user_id), request=request)} |
|
41 | ${h.secure_form(h.route_path('edit_user_auth_tokens_delete', user_id=c.user.user_id), request=request)} | |
42 | ${h.hidden('del_auth_token', auth_token.user_api_key_id)} |
|
42 | ${h.hidden('del_auth_token', auth_token.user_api_key_id)} | |
43 | <button class="btn btn-link btn-danger" type="submit" |
|
43 | <button class="btn btn-link btn-danger" type="submit" | |
44 | onclick="return confirm('${_('Confirm to remove this auth token: %s') % auth_token.token_obfuscated}');"> |
|
44 | onclick="return confirm('${_('Confirm to remove this auth token: %s') % auth_token.token_obfuscated}');"> | |
45 | ${_('Delete')} |
|
45 | ${_('Delete')} | |
46 | </button> |
|
46 | </button> | |
47 | ${h.end_form()} |
|
47 | ${h.end_form()} | |
48 | </td> |
|
48 | </td> | |
49 | </tr> |
|
49 | </tr> | |
50 | %endfor |
|
50 | %endfor | |
51 | %else: |
|
51 | %else: | |
52 | <tr><td><div class="ip">${_('No additional auth tokens specified')}</div></td></tr> |
|
52 | <tr><td><div class="ip">${_('No additional auth tokens specified')}</div></td></tr> | |
53 | %endif |
|
53 | %endif | |
54 | </table> |
|
54 | </table> | |
55 | </div> |
|
55 | </div> | |
56 |
|
56 | |||
57 | <div class="user_auth_tokens"> |
|
57 | <div class="user_auth_tokens"> | |
58 | ${h.secure_form(h.route_path('edit_user_auth_tokens_add', user_id=c.user.user_id), request=request)} |
|
58 | ${h.secure_form(h.route_path('edit_user_auth_tokens_add', user_id=c.user.user_id), request=request)} | |
59 | <div class="form form-vertical"> |
|
59 | <div class="form form-vertical"> | |
60 | <!-- fields --> |
|
60 | <!-- fields --> | |
61 | <div class="fields"> |
|
61 | <div class="fields"> | |
62 | <div class="field"> |
|
62 | <div class="field"> | |
63 | <div class="label"> |
|
63 | <div class="label"> | |
64 | <label for="new_email">${_('New authentication token')}:</label> |
|
64 | <label for="new_email">${_('New authentication token')}:</label> | |
65 | </div> |
|
65 | </div> | |
66 | <div class="input"> |
|
66 | <div class="input"> | |
67 | ${h.text('description', class_='medium', placeholder=_('Description'))} |
|
67 | ${h.text('description', class_='medium', placeholder=_('Description'))} | |
68 | ${h.hidden('lifetime')} |
|
68 | ${h.hidden('lifetime')} | |
69 | ${h.select('role', '', c.role_options)} |
|
69 | ${h.select('role', '', c.role_options)} | |
70 |
|
70 | |||
71 | % if c.allow_scoped_tokens: |
|
71 | % if c.allow_scoped_tokens: | |
72 | ${h.hidden('scope_repo_id')} |
|
72 | ${h.hidden('scope_repo_id')} | |
73 | % else: |
|
73 | % else: | |
74 | ${h.select('scope_repo_id_disabled', '', ['Scopes available in EE edition'], disabled='disabled')} |
|
74 | ${h.select('scope_repo_id_disabled', '', ['Scopes available in EE edition'], disabled='disabled')} | |
75 | % endif |
|
75 | % endif | |
76 | </div> |
|
76 | </div> | |
77 | <p class="help-block"> |
|
77 | <p class="help-block"> | |
78 | ${_('Repository scope works only with tokens with VCS type.')} |
|
78 | ${_('Repository scope works only with tokens with VCS type.')} | |
79 | </p> |
|
79 | </p> | |
80 | </div> |
|
80 | </div> | |
81 | <div class="buttons"> |
|
81 | <div class="buttons"> | |
82 | ${h.submit('save',_('Add'),class_="btn")} |
|
82 | ${h.submit('save',_('Add'),class_="btn")} | |
83 | ${h.reset('reset',_('Reset'),class_="btn")} |
|
83 | ${h.reset('reset',_('Reset'),class_="btn")} | |
84 | </div> |
|
84 | </div> | |
85 | </div> |
|
85 | </div> | |
86 | </div> |
|
86 | </div> | |
87 | ${h.end_form()} |
|
87 | ${h.end_form()} | |
88 | </div> |
|
88 | </div> | |
89 | </div> |
|
89 | </div> | |
90 | </div> |
|
90 | </div> | |
91 |
|
91 | |||
92 | <script> |
|
92 | <script> | |
93 |
|
93 | |||
94 | $(document).ready(function(){ |
|
94 | $(document).ready(function(){ | |
95 |
|
95 | |||
96 | var select2Options = { |
|
96 | var select2Options = { | |
97 | 'containerCssClass': "drop-menu", |
|
97 | 'containerCssClass': "drop-menu", | |
98 | 'dropdownCssClass': "drop-menu-dropdown", |
|
98 | 'dropdownCssClass': "drop-menu-dropdown", | |
99 | 'dropdownAutoWidth': true |
|
99 | 'dropdownAutoWidth': true | |
100 | }; |
|
100 | }; | |
101 | $("#role").select2(select2Options); |
|
101 | $("#role").select2(select2Options); | |
102 |
|
102 | |||
103 | var preloadData = { |
|
103 | var preloadData = { | |
104 | results: [ |
|
104 | results: [ | |
105 | % for entry in c.lifetime_values: |
|
105 | % for entry in c.lifetime_values: | |
106 | {id:${entry[0]}, text:"${entry[1]}"}${'' if loop.last else ','} |
|
106 | {id:${entry[0]}, text:"${entry[1]}"}${'' if loop.last else ','} | |
107 | % endfor |
|
107 | % endfor | |
108 | ] |
|
108 | ] | |
109 | }; |
|
109 | }; | |
110 |
|
110 | |||
111 | $("#lifetime").select2({ |
|
111 | $("#lifetime").select2({ | |
112 | containerCssClass: "drop-menu", |
|
112 | containerCssClass: "drop-menu", | |
113 | dropdownCssClass: "drop-menu-dropdown", |
|
113 | dropdownCssClass: "drop-menu-dropdown", | |
114 | dropdownAutoWidth: true, |
|
114 | dropdownAutoWidth: true, | |
115 | data: preloadData, |
|
115 | data: preloadData, | |
116 | placeholder: "${_('Select or enter expiration date')}", |
|
116 | placeholder: "${_('Select or enter expiration date')}", | |
117 | query: function(query) { |
|
117 | query: function(query) { | |
118 | feedLifetimeOptions(query, preloadData); |
|
118 | feedLifetimeOptions(query, preloadData); | |
119 | } |
|
119 | } | |
120 | }); |
|
120 | }); | |
121 |
|
121 | |||
122 |
|
122 | |||
123 | var repoFilter = function(data) { |
|
123 | var repoFilter = function(data) { | |
124 | var results = []; |
|
124 | var results = []; | |
125 |
|
125 | |||
126 | if (!data.results[0]) { |
|
126 | if (!data.results[0]) { | |
127 | return data |
|
127 | return data | |
128 | } |
|
128 | } | |
129 |
|
129 | |||
130 | $.each(data.results[0].children, function() { |
|
130 | $.each(data.results[0].children, function() { | |
131 | // replace name to ID for submision |
|
131 | // replace name to ID for submision | |
132 |
this.id = this. |
|
132 | this.id = this.repo_id; | |
133 | results.push(this); |
|
133 | results.push(this); | |
134 | }); |
|
134 | }); | |
135 |
|
135 | |||
136 | data.results[0].children = results; |
|
136 | data.results[0].children = results; | |
137 | return data; |
|
137 | return data; | |
138 | }; |
|
138 | }; | |
139 |
|
139 | |||
140 | $("#scope_repo_id_disabled").select2(select2Options); |
|
140 | $("#scope_repo_id_disabled").select2(select2Options); | |
141 |
|
141 | |||
142 | var selectVcsScope = function() { |
|
142 | var selectVcsScope = function() { | |
143 | // select vcs scope and disable input |
|
143 | // select vcs scope and disable input | |
144 | $("#role").select2("val", "${c.role_vcs}").trigger('change'); |
|
144 | $("#role").select2("val", "${c.role_vcs}").trigger('change'); | |
145 | $("#role").select2("readonly", true) |
|
145 | $("#role").select2("readonly", true) | |
146 | }; |
|
146 | }; | |
147 |
|
147 | |||
148 | $("#scope_repo_id").select2({ |
|
148 | $("#scope_repo_id").select2({ | |
149 | cachedDataSource: {}, |
|
149 | cachedDataSource: {}, | |
150 | minimumInputLength: 2, |
|
150 | minimumInputLength: 2, | |
151 | placeholder: "${_('repository scope')}", |
|
151 | placeholder: "${_('repository scope')}", | |
152 | dropdownAutoWidth: true, |
|
152 | dropdownAutoWidth: true, | |
153 | containerCssClass: "drop-menu", |
|
153 | containerCssClass: "drop-menu", | |
154 | dropdownCssClass: "drop-menu-dropdown", |
|
154 | dropdownCssClass: "drop-menu-dropdown", | |
155 | formatResult: formatResult, |
|
155 | formatResult: formatRepoResult, | |
156 | query: $.debounce(250, function(query){ |
|
156 | query: $.debounce(250, function(query){ | |
157 | self = this; |
|
157 | self = this; | |
158 | var cacheKey = query.term; |
|
158 | var cacheKey = query.term; | |
159 | var cachedData = self.cachedDataSource[cacheKey]; |
|
159 | var cachedData = self.cachedDataSource[cacheKey]; | |
160 |
|
160 | |||
161 | if (cachedData) { |
|
161 | if (cachedData) { | |
162 | query.callback({results: cachedData.results}); |
|
162 | query.callback({results: cachedData.results}); | |
163 | } else { |
|
163 | } else { | |
164 | $.ajax({ |
|
164 | $.ajax({ | |
165 | url: pyroutes.url('repo_list_data'), |
|
165 | url: pyroutes.url('repo_list_data'), | |
166 | data: {'query': query.term}, |
|
166 | data: {'query': query.term}, | |
167 | dataType: 'json', |
|
167 | dataType: 'json', | |
168 | type: 'GET', |
|
168 | type: 'GET', | |
169 | success: function(data) { |
|
169 | success: function(data) { | |
170 | data = repoFilter(data); |
|
170 | data = repoFilter(data); | |
171 | self.cachedDataSource[cacheKey] = data; |
|
171 | self.cachedDataSource[cacheKey] = data; | |
172 | query.callback({results: data.results}); |
|
172 | query.callback({results: data.results}); | |
173 | }, |
|
173 | }, | |
174 | error: function(data, textStatus, errorThrown) { |
|
174 | error: function(data, textStatus, errorThrown) { | |
175 | alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText)); |
|
175 | alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText)); | |
176 | } |
|
176 | } | |
177 | }) |
|
177 | }) | |
178 | } |
|
178 | } | |
179 | }) |
|
179 | }) | |
180 | }); |
|
180 | }); | |
181 | $("#scope_repo_id").on('select2-selecting', function(e){ |
|
181 | $("#scope_repo_id").on('select2-selecting', function(e){ | |
182 | selectVcsScope() |
|
182 | selectVcsScope() | |
183 | }); |
|
183 | }); | |
184 |
|
184 | |||
185 | }); |
|
185 | }); | |
186 | </script> |
|
186 | </script> |
@@ -1,613 +1,662 b'' | |||||
1 | ## -*- coding: utf-8 -*- |
|
1 | ## -*- coding: utf-8 -*- | |
2 | <%inherit file="root.mako"/> |
|
2 | <%inherit file="root.mako"/> | |
3 |
|
3 | |||
4 | <%include file="/ejs_templates/templates.html"/> |
|
4 | <%include file="/ejs_templates/templates.html"/> | |
5 |
|
5 | |||
6 | <div class="outerwrapper"> |
|
6 | <div class="outerwrapper"> | |
7 | <!-- HEADER --> |
|
7 | <!-- HEADER --> | |
8 | <div class="header"> |
|
8 | <div class="header"> | |
9 | <div id="header-inner" class="wrapper"> |
|
9 | <div id="header-inner" class="wrapper"> | |
10 | <div id="logo"> |
|
10 | <div id="logo"> | |
11 | <div class="logo-wrapper"> |
|
11 | <div class="logo-wrapper"> | |
12 | <a href="${h.route_path('home')}"><img src="${h.asset('images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a> |
|
12 | <a href="${h.route_path('home')}"><img src="${h.asset('images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a> | |
13 | </div> |
|
13 | </div> | |
14 | %if c.rhodecode_name: |
|
14 | %if c.rhodecode_name: | |
15 | <div class="branding">- ${h.branding(c.rhodecode_name)}</div> |
|
15 | <div class="branding">- ${h.branding(c.rhodecode_name)}</div> | |
16 | %endif |
|
16 | %endif | |
17 | </div> |
|
17 | </div> | |
18 | <!-- MENU BAR NAV --> |
|
18 | <!-- MENU BAR NAV --> | |
19 | ${self.menu_bar_nav()} |
|
19 | ${self.menu_bar_nav()} | |
20 | <!-- END MENU BAR NAV --> |
|
20 | <!-- END MENU BAR NAV --> | |
21 | </div> |
|
21 | </div> | |
22 | </div> |
|
22 | </div> | |
23 | ${self.menu_bar_subnav()} |
|
23 | ${self.menu_bar_subnav()} | |
24 | <!-- END HEADER --> |
|
24 | <!-- END HEADER --> | |
25 |
|
25 | |||
26 | <!-- CONTENT --> |
|
26 | <!-- CONTENT --> | |
27 | <div id="content" class="wrapper"> |
|
27 | <div id="content" class="wrapper"> | |
28 |
|
28 | |||
29 | <rhodecode-toast id="notifications"></rhodecode-toast> |
|
29 | <rhodecode-toast id="notifications"></rhodecode-toast> | |
30 |
|
30 | |||
31 | <div class="main"> |
|
31 | <div class="main"> | |
32 | ${next.main()} |
|
32 | ${next.main()} | |
33 | </div> |
|
33 | </div> | |
34 | </div> |
|
34 | </div> | |
35 | <!-- END CONTENT --> |
|
35 | <!-- END CONTENT --> | |
36 |
|
36 | |||
37 | </div> |
|
37 | </div> | |
38 | <!-- FOOTER --> |
|
38 | <!-- FOOTER --> | |
39 | <div id="footer"> |
|
39 | <div id="footer"> | |
40 | <div id="footer-inner" class="title wrapper"> |
|
40 | <div id="footer-inner" class="title wrapper"> | |
41 | <div> |
|
41 | <div> | |
42 | <p class="footer-link-right"> |
|
42 | <p class="footer-link-right"> | |
43 | % if c.visual.show_version: |
|
43 | % if c.visual.show_version: | |
44 | RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition} |
|
44 | RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition} | |
45 | % endif |
|
45 | % endif | |
46 | © 2010-${h.datetime.today().year}, <a href="${h.route_url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved. |
|
46 | © 2010-${h.datetime.today().year}, <a href="${h.route_url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved. | |
47 | % if c.visual.rhodecode_support_url: |
|
47 | % if c.visual.rhodecode_support_url: | |
48 | <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a> |
|
48 | <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a> | |
49 | % endif |
|
49 | % endif | |
50 | </p> |
|
50 | </p> | |
51 | <% sid = 'block' if request.GET.get('showrcid') else 'none' %> |
|
51 | <% sid = 'block' if request.GET.get('showrcid') else 'none' %> | |
52 | <p class="server-instance" style="display:${sid}"> |
|
52 | <p class="server-instance" style="display:${sid}"> | |
53 | ## display hidden instance ID if specially defined |
|
53 | ## display hidden instance ID if specially defined | |
54 | % if c.rhodecode_instanceid: |
|
54 | % if c.rhodecode_instanceid: | |
55 | ${_('RhodeCode instance id: %s') % c.rhodecode_instanceid} |
|
55 | ${_('RhodeCode instance id: %s') % c.rhodecode_instanceid} | |
56 | % endif |
|
56 | % endif | |
57 | </p> |
|
57 | </p> | |
58 | </div> |
|
58 | </div> | |
59 | </div> |
|
59 | </div> | |
60 | </div> |
|
60 | </div> | |
61 |
|
61 | |||
62 | <!-- END FOOTER --> |
|
62 | <!-- END FOOTER --> | |
63 |
|
63 | |||
64 | ### MAKO DEFS ### |
|
64 | ### MAKO DEFS ### | |
65 |
|
65 | |||
66 | <%def name="menu_bar_subnav()"> |
|
66 | <%def name="menu_bar_subnav()"> | |
67 | </%def> |
|
67 | </%def> | |
68 |
|
68 | |||
69 | <%def name="breadcrumbs(class_='breadcrumbs')"> |
|
69 | <%def name="breadcrumbs(class_='breadcrumbs')"> | |
70 | <div class="${class_}"> |
|
70 | <div class="${class_}"> | |
71 | ${self.breadcrumbs_links()} |
|
71 | ${self.breadcrumbs_links()} | |
72 | </div> |
|
72 | </div> | |
73 | </%def> |
|
73 | </%def> | |
74 |
|
74 | |||
75 | <%def name="admin_menu()"> |
|
75 | <%def name="admin_menu()"> | |
76 | <ul class="admin_menu submenu"> |
|
76 | <ul class="admin_menu submenu"> | |
77 | <li><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li> |
|
77 | <li><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li> | |
78 | <li><a href="${h.route_path('repos')}">${_('Repositories')}</a></li> |
|
78 | <li><a href="${h.route_path('repos')}">${_('Repositories')}</a></li> | |
79 | <li><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li> |
|
79 | <li><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li> | |
80 | <li><a href="${h.route_path('users')}">${_('Users')}</a></li> |
|
80 | <li><a href="${h.route_path('users')}">${_('Users')}</a></li> | |
81 | <li><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li> |
|
81 | <li><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li> | |
82 | <li><a href="${h.route_path('admin_permissions_application')}">${_('Permissions')}</a></li> |
|
82 | <li><a href="${h.route_path('admin_permissions_application')}">${_('Permissions')}</a></li> | |
83 | <li><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li> |
|
83 | <li><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li> | |
84 | <li><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li> |
|
84 | <li><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li> | |
85 | <li><a href="${h.route_path('admin_defaults_repositories')}">${_('Defaults')}</a></li> |
|
85 | <li><a href="${h.route_path('admin_defaults_repositories')}">${_('Defaults')}</a></li> | |
86 | <li class="last"><a href="${h.route_path('admin_settings')}">${_('Settings')}</a></li> |
|
86 | <li class="last"><a href="${h.route_path('admin_settings')}">${_('Settings')}</a></li> | |
87 | </ul> |
|
87 | </ul> | |
88 | </%def> |
|
88 | </%def> | |
89 |
|
89 | |||
90 |
|
90 | |||
91 | <%def name="dt_info_panel(elements)"> |
|
91 | <%def name="dt_info_panel(elements)"> | |
92 | <dl class="dl-horizontal"> |
|
92 | <dl class="dl-horizontal"> | |
93 | %for dt, dd, title, show_items in elements: |
|
93 | %for dt, dd, title, show_items in elements: | |
94 | <dt>${dt}:</dt> |
|
94 | <dt>${dt}:</dt> | |
95 | <dd title="${h.tooltip(title)}"> |
|
95 | <dd title="${h.tooltip(title)}"> | |
96 | %if callable(dd): |
|
96 | %if callable(dd): | |
97 | ## allow lazy evaluation of elements |
|
97 | ## allow lazy evaluation of elements | |
98 | ${dd()} |
|
98 | ${dd()} | |
99 | %else: |
|
99 | %else: | |
100 | ${dd} |
|
100 | ${dd} | |
101 | %endif |
|
101 | %endif | |
102 | %if show_items: |
|
102 | %if show_items: | |
103 | <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span> |
|
103 | <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span> | |
104 | %endif |
|
104 | %endif | |
105 | </dd> |
|
105 | </dd> | |
106 |
|
106 | |||
107 | %if show_items: |
|
107 | %if show_items: | |
108 | <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none"> |
|
108 | <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none"> | |
109 | %for item in show_items: |
|
109 | %for item in show_items: | |
110 | <dt></dt> |
|
110 | <dt></dt> | |
111 | <dd>${item}</dd> |
|
111 | <dd>${item}</dd> | |
112 | %endfor |
|
112 | %endfor | |
113 | </div> |
|
113 | </div> | |
114 | %endif |
|
114 | %endif | |
115 |
|
115 | |||
116 | %endfor |
|
116 | %endfor | |
117 | </dl> |
|
117 | </dl> | |
118 | </%def> |
|
118 | </%def> | |
119 |
|
119 | |||
120 |
|
120 | |||
121 | <%def name="gravatar(email, size=16)"> |
|
121 | <%def name="gravatar(email, size=16)"> | |
122 | <% |
|
122 | <% | |
123 | if (size > 16): |
|
123 | if (size > 16): | |
124 | gravatar_class = 'gravatar gravatar-large' |
|
124 | gravatar_class = 'gravatar gravatar-large' | |
125 | else: |
|
125 | else: | |
126 | gravatar_class = 'gravatar' |
|
126 | gravatar_class = 'gravatar' | |
127 | %> |
|
127 | %> | |
128 | <%doc> |
|
128 | <%doc> | |
129 | TODO: johbo: For now we serve double size images to make it smooth |
|
129 | TODO: johbo: For now we serve double size images to make it smooth | |
130 | for retina. This is how it worked until now. Should be replaced |
|
130 | for retina. This is how it worked until now. Should be replaced | |
131 | with a better solution at some point. |
|
131 | with a better solution at some point. | |
132 | </%doc> |
|
132 | </%doc> | |
133 | <img class="${gravatar_class}" src="${h.gravatar_url(email, size * 2)}" height="${size}" width="${size}"> |
|
133 | <img class="${gravatar_class}" src="${h.gravatar_url(email, size * 2)}" height="${size}" width="${size}"> | |
134 | </%def> |
|
134 | </%def> | |
135 |
|
135 | |||
136 |
|
136 | |||
137 | <%def name="gravatar_with_user(contact, size=16, show_disabled=False)"> |
|
137 | <%def name="gravatar_with_user(contact, size=16, show_disabled=False)"> | |
138 | <% email = h.email_or_none(contact) %> |
|
138 | <% email = h.email_or_none(contact) %> | |
139 | <div class="rc-user tooltip" title="${h.tooltip(h.author_string(email))}"> |
|
139 | <div class="rc-user tooltip" title="${h.tooltip(h.author_string(email))}"> | |
140 | ${self.gravatar(email, size)} |
|
140 | ${self.gravatar(email, size)} | |
141 | <span class="${'user user-disabled' if show_disabled else 'user'}"> ${h.link_to_user(contact)}</span> |
|
141 | <span class="${'user user-disabled' if show_disabled else 'user'}"> ${h.link_to_user(contact)}</span> | |
142 | </div> |
|
142 | </div> | |
143 | </%def> |
|
143 | </%def> | |
144 |
|
144 | |||
145 |
|
145 | |||
146 | ## admin menu used for people that have some admin resources |
|
146 | ## admin menu used for people that have some admin resources | |
147 | <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)"> |
|
147 | <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)"> | |
148 | <ul class="submenu"> |
|
148 | <ul class="submenu"> | |
149 | %if repositories: |
|
149 | %if repositories: | |
150 | <li class="local-admin-repos"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li> |
|
150 | <li class="local-admin-repos"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li> | |
151 | %endif |
|
151 | %endif | |
152 | %if repository_groups: |
|
152 | %if repository_groups: | |
153 | <li class="local-admin-repo-groups"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li> |
|
153 | <li class="local-admin-repo-groups"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li> | |
154 | %endif |
|
154 | %endif | |
155 | %if user_groups: |
|
155 | %if user_groups: | |
156 | <li class="local-admin-user-groups"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li> |
|
156 | <li class="local-admin-user-groups"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li> | |
157 | %endif |
|
157 | %endif | |
158 | </ul> |
|
158 | </ul> | |
159 | </%def> |
|
159 | </%def> | |
160 |
|
160 | |||
161 | <%def name="repo_page_title(repo_instance)"> |
|
161 | <%def name="repo_page_title(repo_instance)"> | |
162 | <div class="title-content"> |
|
162 | <div class="title-content"> | |
163 | <div class="title-main"> |
|
163 | <div class="title-main"> | |
164 | ## SVN/HG/GIT icons |
|
164 | ## SVN/HG/GIT icons | |
165 | %if h.is_hg(repo_instance): |
|
165 | %if h.is_hg(repo_instance): | |
166 | <i class="icon-hg"></i> |
|
166 | <i class="icon-hg"></i> | |
167 | %endif |
|
167 | %endif | |
168 | %if h.is_git(repo_instance): |
|
168 | %if h.is_git(repo_instance): | |
169 | <i class="icon-git"></i> |
|
169 | <i class="icon-git"></i> | |
170 | %endif |
|
170 | %endif | |
171 | %if h.is_svn(repo_instance): |
|
171 | %if h.is_svn(repo_instance): | |
172 | <i class="icon-svn"></i> |
|
172 | <i class="icon-svn"></i> | |
173 | %endif |
|
173 | %endif | |
174 |
|
174 | |||
175 | ## public/private |
|
175 | ## public/private | |
176 | %if repo_instance.private: |
|
176 | %if repo_instance.private: | |
177 | <i class="icon-repo-private"></i> |
|
177 | <i class="icon-repo-private"></i> | |
178 | %else: |
|
178 | %else: | |
179 | <i class="icon-repo-public"></i> |
|
179 | <i class="icon-repo-public"></i> | |
180 | %endif |
|
180 | %endif | |
181 |
|
181 | |||
182 | ## repo name with group name |
|
182 | ## repo name with group name | |
183 | ${h.breadcrumb_repo_link(c.rhodecode_db_repo)} |
|
183 | ${h.breadcrumb_repo_link(c.rhodecode_db_repo)} | |
184 |
|
184 | |||
185 | </div> |
|
185 | </div> | |
186 |
|
186 | |||
187 | ## FORKED |
|
187 | ## FORKED | |
188 | %if repo_instance.fork: |
|
188 | %if repo_instance.fork: | |
189 | <p> |
|
189 | <p> | |
190 | <i class="icon-code-fork"></i> ${_('Fork of')} |
|
190 | <i class="icon-code-fork"></i> ${_('Fork of')} | |
191 | <a href="${h.route_path('repo_summary',repo_name=repo_instance.fork.repo_name)}">${repo_instance.fork.repo_name}</a> |
|
191 | <a href="${h.route_path('repo_summary',repo_name=repo_instance.fork.repo_name)}">${repo_instance.fork.repo_name}</a> | |
192 | </p> |
|
192 | </p> | |
193 | %endif |
|
193 | %endif | |
194 |
|
194 | |||
195 | ## IMPORTED FROM REMOTE |
|
195 | ## IMPORTED FROM REMOTE | |
196 | %if repo_instance.clone_uri: |
|
196 | %if repo_instance.clone_uri: | |
197 | <p> |
|
197 | <p> | |
198 | <i class="icon-code-fork"></i> ${_('Clone from')} |
|
198 | <i class="icon-code-fork"></i> ${_('Clone from')} | |
199 | <a href="${h.safe_str(h.hide_credentials(repo_instance.clone_uri))}">${h.hide_credentials(repo_instance.clone_uri)}</a> |
|
199 | <a href="${h.safe_str(h.hide_credentials(repo_instance.clone_uri))}">${h.hide_credentials(repo_instance.clone_uri)}</a> | |
200 | </p> |
|
200 | </p> | |
201 | %endif |
|
201 | %endif | |
202 |
|
202 | |||
203 | ## LOCKING STATUS |
|
203 | ## LOCKING STATUS | |
204 | %if repo_instance.locked[0]: |
|
204 | %if repo_instance.locked[0]: | |
205 | <p class="locking_locked"> |
|
205 | <p class="locking_locked"> | |
206 | <i class="icon-repo-lock"></i> |
|
206 | <i class="icon-repo-lock"></i> | |
207 | ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}} |
|
207 | ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}} | |
208 | </p> |
|
208 | </p> | |
209 | %elif repo_instance.enable_locking: |
|
209 | %elif repo_instance.enable_locking: | |
210 | <p class="locking_unlocked"> |
|
210 | <p class="locking_unlocked"> | |
211 | <i class="icon-repo-unlock"></i> |
|
211 | <i class="icon-repo-unlock"></i> | |
212 | ${_('Repository not locked. Pull repository to lock it.')} |
|
212 | ${_('Repository not locked. Pull repository to lock it.')} | |
213 | </p> |
|
213 | </p> | |
214 | %endif |
|
214 | %endif | |
215 |
|
215 | |||
216 | </div> |
|
216 | </div> | |
217 | </%def> |
|
217 | </%def> | |
218 |
|
218 | |||
219 | <%def name="repo_menu(active=None)"> |
|
219 | <%def name="repo_menu(active=None)"> | |
220 | <% |
|
220 | <% | |
221 | def is_active(selected): |
|
221 | def is_active(selected): | |
222 | if selected == active: |
|
222 | if selected == active: | |
223 | return "active" |
|
223 | return "active" | |
224 | %> |
|
224 | %> | |
225 |
|
225 | |||
226 | <!--- CONTEXT BAR --> |
|
226 | <!--- CONTEXT BAR --> | |
227 | <div id="context-bar"> |
|
227 | <div id="context-bar"> | |
228 | <div class="wrapper"> |
|
228 | <div class="wrapper"> | |
229 |
<ul id="context-pages" class="horizontal-list |
|
229 | <ul id="context-pages" class="navigation horizontal-list"> | |
230 | <li class="${is_active('summary')}"><a class="menulink" href="${h.route_path('repo_summary', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li> |
|
230 | <li class="${is_active('summary')}"><a class="menulink" href="${h.route_path('repo_summary', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li> | |
231 | <li class="${is_active('changelog')}"><a class="menulink" href="${h.route_path('repo_changelog', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li> |
|
231 | <li class="${is_active('changelog')}"><a class="menulink" href="${h.route_path('repo_changelog', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li> | |
232 | <li class="${is_active('files')}"><a class="menulink" href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.rhodecode_db_repo.landing_rev[1], f_path='')}"><div class="menulabel">${_('Files')}</div></a></li> |
|
232 | <li class="${is_active('files')}"><a class="menulink" href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.rhodecode_db_repo.landing_rev[1], f_path='')}"><div class="menulabel">${_('Files')}</div></a></li> | |
233 | <li class="${is_active('compare')}"><a class="menulink" href="${h.route_path('repo_compare_select',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a></li> |
|
233 | <li class="${is_active('compare')}"><a class="menulink" href="${h.route_path('repo_compare_select',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a></li> | |
234 | ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()" |
|
234 | ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()" | |
235 | %if c.rhodecode_db_repo.repo_type in ['git','hg']: |
|
235 | %if c.rhodecode_db_repo.repo_type in ['git','hg']: | |
236 | <li class="${is_active('showpullrequest')}"> |
|
236 | <li class="${is_active('showpullrequest')}"> | |
237 | <a class="menulink" href="${h.route_path('pullrequest_show_all', repo_name=c.repo_name)}" title="${h.tooltip(_('Show Pull Requests for %s') % c.repo_name)}"> |
|
237 | <a class="menulink" href="${h.route_path('pullrequest_show_all', repo_name=c.repo_name)}" title="${h.tooltip(_('Show Pull Requests for %s') % c.repo_name)}"> | |
238 | %if c.repository_pull_requests: |
|
238 | %if c.repository_pull_requests: | |
239 | <span class="pr_notifications">${c.repository_pull_requests}</span> |
|
239 | <span class="pr_notifications">${c.repository_pull_requests}</span> | |
240 | %endif |
|
240 | %endif | |
241 | <div class="menulabel">${_('Pull Requests')}</div> |
|
241 | <div class="menulabel">${_('Pull Requests')}</div> | |
242 | </a> |
|
242 | </a> | |
243 | </li> |
|
243 | </li> | |
244 | %endif |
|
244 | %endif | |
245 | <li class="${is_active('options')}"> |
|
245 | <li class="${is_active('options')}"> | |
246 | <a class="menulink dropdown"> |
|
246 | <a class="menulink dropdown"> | |
247 | <div class="menulabel">${_('Options')} <div class="show_more"></div></div> |
|
247 | <div class="menulabel">${_('Options')} <div class="show_more"></div></div> | |
248 | </a> |
|
248 | </a> | |
249 | <ul class="submenu"> |
|
249 | <ul class="submenu"> | |
250 | %if h.HasRepoPermissionAll('repository.admin')(c.repo_name): |
|
250 | %if h.HasRepoPermissionAll('repository.admin')(c.repo_name): | |
251 | <li><a href="${h.route_path('edit_repo',repo_name=c.repo_name)}">${_('Settings')}</a></li> |
|
251 | <li><a href="${h.route_path('edit_repo',repo_name=c.repo_name)}">${_('Settings')}</a></li> | |
252 | %endif |
|
252 | %endif | |
253 | %if c.rhodecode_db_repo.fork: |
|
253 | %if c.rhodecode_db_repo.fork: | |
254 | <li> |
|
254 | <li> | |
255 | <a title="${h.tooltip(_('Compare fork with %s' % c.rhodecode_db_repo.fork.repo_name))}" |
|
255 | <a title="${h.tooltip(_('Compare fork with %s' % c.rhodecode_db_repo.fork.repo_name))}" | |
256 | href="${h.route_path('repo_compare', |
|
256 | href="${h.route_path('repo_compare', | |
257 | repo_name=c.rhodecode_db_repo.fork.repo_name, |
|
257 | repo_name=c.rhodecode_db_repo.fork.repo_name, | |
258 | source_ref_type=c.rhodecode_db_repo.landing_rev[0], |
|
258 | source_ref_type=c.rhodecode_db_repo.landing_rev[0], | |
259 | source_ref=c.rhodecode_db_repo.landing_rev[1], |
|
259 | source_ref=c.rhodecode_db_repo.landing_rev[1], | |
260 | target_repo=c.repo_name,target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0], |
|
260 | target_repo=c.repo_name,target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0], | |
261 | target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1], |
|
261 | target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1], | |
262 | _query=dict(merge=1))}" |
|
262 | _query=dict(merge=1))}" | |
263 | > |
|
263 | > | |
264 | ${_('Compare fork')} |
|
264 | ${_('Compare fork')} | |
265 | </a> |
|
265 | </a> | |
266 | </li> |
|
266 | </li> | |
267 | %endif |
|
267 | %endif | |
268 |
|
268 | |||
269 | <li><a href="${h.route_path('search_repo',repo_name=c.repo_name)}">${_('Search')}</a></li> |
|
269 | <li><a href="${h.route_path('search_repo',repo_name=c.repo_name)}">${_('Search')}</a></li> | |
270 |
|
270 | |||
271 | %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking: |
|
271 | %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking: | |
272 | %if c.rhodecode_db_repo.locked[0]: |
|
272 | %if c.rhodecode_db_repo.locked[0]: | |
273 | <li><a class="locking_del" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li> |
|
273 | <li><a class="locking_del" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li> | |
274 | %else: |
|
274 | %else: | |
275 | <li><a class="locking_add" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li> |
|
275 | <li><a class="locking_add" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li> | |
276 | %endif |
|
276 | %endif | |
277 | %endif |
|
277 | %endif | |
278 | %if c.rhodecode_user.username != h.DEFAULT_USER: |
|
278 | %if c.rhodecode_user.username != h.DEFAULT_USER: | |
279 | %if c.rhodecode_db_repo.repo_type in ['git','hg']: |
|
279 | %if c.rhodecode_db_repo.repo_type in ['git','hg']: | |
280 | <li><a href="${h.route_path('repo_fork_new',repo_name=c.repo_name)}">${_('Fork')}</a></li> |
|
280 | <li><a href="${h.route_path('repo_fork_new',repo_name=c.repo_name)}">${_('Fork')}</a></li> | |
281 | <li><a href="${h.route_path('pullrequest_new',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li> |
|
281 | <li><a href="${h.route_path('pullrequest_new',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li> | |
282 | %endif |
|
282 | %endif | |
283 | %endif |
|
283 | %endif | |
284 | </ul> |
|
284 | </ul> | |
285 | </li> |
|
285 | </li> | |
286 | </ul> |
|
286 | </ul> | |
287 | </div> |
|
287 | </div> | |
288 | <div class="clear"></div> |
|
288 | <div class="clear"></div> | |
289 | </div> |
|
289 | </div> | |
290 | <!--- END CONTEXT BAR --> |
|
290 | <!--- END CONTEXT BAR --> | |
291 |
|
291 | |||
292 | </%def> |
|
292 | </%def> | |
293 |
|
293 | |||
294 | <%def name="usermenu(active=False)"> |
|
294 | <%def name="usermenu(active=False)"> | |
295 | ## USER MENU |
|
295 | ## USER MENU | |
296 | <li id="quick_login_li" class="${'active' if active else ''}"> |
|
296 | <li id="quick_login_li" class="${'active' if active else ''}"> | |
297 | <a id="quick_login_link" class="menulink childs"> |
|
297 | <a id="quick_login_link" class="menulink childs"> | |
298 | ${gravatar(c.rhodecode_user.email, 20)} |
|
298 | ${gravatar(c.rhodecode_user.email, 20)} | |
299 | <span class="user"> |
|
299 | <span class="user"> | |
300 | %if c.rhodecode_user.username != h.DEFAULT_USER: |
|
300 | %if c.rhodecode_user.username != h.DEFAULT_USER: | |
301 | <span class="menu_link_user">${c.rhodecode_user.username}</span><div class="show_more"></div> |
|
301 | <span class="menu_link_user">${c.rhodecode_user.username}</span><div class="show_more"></div> | |
302 | %else: |
|
302 | %else: | |
303 | <span>${_('Sign in')}</span> |
|
303 | <span>${_('Sign in')}</span> | |
304 | %endif |
|
304 | %endif | |
305 | </span> |
|
305 | </span> | |
306 | </a> |
|
306 | </a> | |
307 |
|
307 | |||
308 | <div class="user-menu submenu"> |
|
308 | <div class="user-menu submenu"> | |
309 | <div id="quick_login"> |
|
309 | <div id="quick_login"> | |
310 | %if c.rhodecode_user.username == h.DEFAULT_USER: |
|
310 | %if c.rhodecode_user.username == h.DEFAULT_USER: | |
311 | <h4>${_('Sign in to your account')}</h4> |
|
311 | <h4>${_('Sign in to your account')}</h4> | |
312 | ${h.form(h.route_path('login', _query={'came_from': h.current_route_path(request)}), needs_csrf_token=False)} |
|
312 | ${h.form(h.route_path('login', _query={'came_from': h.current_route_path(request)}), needs_csrf_token=False)} | |
313 | <div class="form form-vertical"> |
|
313 | <div class="form form-vertical"> | |
314 | <div class="fields"> |
|
314 | <div class="fields"> | |
315 | <div class="field"> |
|
315 | <div class="field"> | |
316 | <div class="label"> |
|
316 | <div class="label"> | |
317 | <label for="username">${_('Username')}:</label> |
|
317 | <label for="username">${_('Username')}:</label> | |
318 | </div> |
|
318 | </div> | |
319 | <div class="input"> |
|
319 | <div class="input"> | |
320 | ${h.text('username',class_='focus',tabindex=1)} |
|
320 | ${h.text('username',class_='focus',tabindex=1)} | |
321 | </div> |
|
321 | </div> | |
322 |
|
322 | |||
323 | </div> |
|
323 | </div> | |
324 | <div class="field"> |
|
324 | <div class="field"> | |
325 | <div class="label"> |
|
325 | <div class="label"> | |
326 | <label for="password">${_('Password')}:</label> |
|
326 | <label for="password">${_('Password')}:</label> | |
327 | %if h.HasPermissionAny('hg.password_reset.enabled')(): |
|
327 | %if h.HasPermissionAny('hg.password_reset.enabled')(): | |
328 | <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'), class_='pwd_reset')}</span> |
|
328 | <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'), class_='pwd_reset')}</span> | |
329 | %endif |
|
329 | %endif | |
330 | </div> |
|
330 | </div> | |
331 | <div class="input"> |
|
331 | <div class="input"> | |
332 | ${h.password('password',class_='focus',tabindex=2)} |
|
332 | ${h.password('password',class_='focus',tabindex=2)} | |
333 | </div> |
|
333 | </div> | |
334 | </div> |
|
334 | </div> | |
335 | <div class="buttons"> |
|
335 | <div class="buttons"> | |
336 | <div class="register"> |
|
336 | <div class="register"> | |
337 | %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')(): |
|
337 | %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')(): | |
338 | ${h.link_to(_("Don't have an account?"),h.route_path('register'))} <br/> |
|
338 | ${h.link_to(_("Don't have an account?"),h.route_path('register'))} <br/> | |
339 | %endif |
|
339 | %endif | |
340 | ${h.link_to(_("Using external auth? Sign In here."),h.route_path('login'))} |
|
340 | ${h.link_to(_("Using external auth? Sign In here."),h.route_path('login'))} | |
341 | </div> |
|
341 | </div> | |
342 | <div class="submit"> |
|
342 | <div class="submit"> | |
343 | ${h.submit('sign_in',_('Sign In'),class_="btn btn-small",tabindex=3)} |
|
343 | ${h.submit('sign_in',_('Sign In'),class_="btn btn-small",tabindex=3)} | |
344 | </div> |
|
344 | </div> | |
345 | </div> |
|
345 | </div> | |
346 | </div> |
|
346 | </div> | |
347 | </div> |
|
347 | </div> | |
348 | ${h.end_form()} |
|
348 | ${h.end_form()} | |
349 | %else: |
|
349 | %else: | |
350 | <div class=""> |
|
350 | <div class=""> | |
351 | <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div> |
|
351 | <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div> | |
352 | <div class="full_name">${c.rhodecode_user.full_name_or_username}</div> |
|
352 | <div class="full_name">${c.rhodecode_user.full_name_or_username}</div> | |
353 | <div class="email">${c.rhodecode_user.email}</div> |
|
353 | <div class="email">${c.rhodecode_user.email}</div> | |
354 | </div> |
|
354 | </div> | |
355 | <div class=""> |
|
355 | <div class=""> | |
356 | <ol class="links"> |
|
356 | <ol class="links"> | |
357 | <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li> |
|
357 | <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li> | |
358 | % if c.rhodecode_user.personal_repo_group: |
|
358 | % if c.rhodecode_user.personal_repo_group: | |
359 | <li>${h.link_to(_(u'My personal group'), h.route_path('repo_group_home', repo_group_name=c.rhodecode_user.personal_repo_group.group_name))}</li> |
|
359 | <li>${h.link_to(_(u'My personal group'), h.route_path('repo_group_home', repo_group_name=c.rhodecode_user.personal_repo_group.group_name))}</li> | |
360 | % endif |
|
360 | % endif | |
361 | <li>${h.link_to(_(u'Pull Requests'), h.route_path('my_account_pullrequests'))}</li> |
|
361 | <li>${h.link_to(_(u'Pull Requests'), h.route_path('my_account_pullrequests'))}</li> | |
362 |
|
362 | |||
363 | <li class="logout"> |
|
363 | <li class="logout"> | |
364 | ${h.secure_form(h.route_path('logout'), request=request)} |
|
364 | ${h.secure_form(h.route_path('logout'), request=request)} | |
365 | ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")} |
|
365 | ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")} | |
366 | ${h.end_form()} |
|
366 | ${h.end_form()} | |
367 | </li> |
|
367 | </li> | |
368 | </ol> |
|
368 | </ol> | |
369 | </div> |
|
369 | </div> | |
370 | %endif |
|
370 | %endif | |
371 | </div> |
|
371 | </div> | |
372 | </div> |
|
372 | </div> | |
373 | %if c.rhodecode_user.username != h.DEFAULT_USER: |
|
373 | %if c.rhodecode_user.username != h.DEFAULT_USER: | |
374 | <div class="pill_container"> |
|
374 | <div class="pill_container"> | |
375 | <a class="menu_link_notifications ${'empty' if c.unread_notifications == 0 else ''}" href="${h.route_path('notifications_show_all')}">${c.unread_notifications}</a> |
|
375 | <a class="menu_link_notifications ${'empty' if c.unread_notifications == 0 else ''}" href="${h.route_path('notifications_show_all')}">${c.unread_notifications}</a> | |
376 | </div> |
|
376 | </div> | |
377 | % endif |
|
377 | % endif | |
378 | </li> |
|
378 | </li> | |
379 | </%def> |
|
379 | </%def> | |
380 |
|
380 | |||
381 | <%def name="menu_items(active=None)"> |
|
381 | <%def name="menu_items(active=None)"> | |
382 | <% |
|
382 | <% | |
383 | def is_active(selected): |
|
383 | def is_active(selected): | |
384 | if selected == active: |
|
384 | if selected == active: | |
385 | return "active" |
|
385 | return "active" | |
386 | return "" |
|
386 | return "" | |
387 | %> |
|
387 | %> | |
388 | <ul id="quick" class="main_nav navigation horizontal-list"> |
|
388 | <ul id="quick" class="main_nav navigation horizontal-list"> | |
389 | <!-- repo switcher --> |
|
389 | ||
390 | <li class="${is_active('repositories')} repo_switcher_li has_select2"> |
|
390 | ## Main filter | |
391 | <input id="repo_switcher" name="repo_switcher" type="hidden"> |
|
391 | <li> | |
|
392 | <div class="menulabel main_filter_box"> | |||
|
393 | <div class="main_filter_input_box"> | |||
|
394 | <input class="main_filter_input" id="main_filter" size="15" type="text" name="main_filter" placeholder="${_('search / go to...')}" value=""/> | |||
|
395 | </div> | |||
|
396 | <div class="main_filter_help_box"> | |||
|
397 | <a href="#showFilterHelp" onclick="showMainFilterBox(); return false">?</a> | |||
|
398 | </div> | |||
|
399 | </div> | |||
|
400 | ||||
|
401 | <div id="main_filter_help" style="display: none"> | |||
|
402 | Use '/' key to quickly access this field. | |||
|
403 | Enter name of repository, or repository group for quick search. | |||
|
404 | ||||
|
405 | Prefix query to allow special search: | |||
|
406 | ||||
|
407 | For usernames, e.g user:admin | |||
|
408 | ||||
|
409 | For commit hash/id, e.g commit:efced4 | |||
|
410 | ||||
|
411 | </div> | |||
392 | </li> |
|
412 | </li> | |
393 |
|
413 | |||
394 | ## ROOT MENU |
|
414 | ## ROOT MENU | |
395 | %if c.rhodecode_user.username != h.DEFAULT_USER: |
|
415 | %if c.rhodecode_user.username != h.DEFAULT_USER: | |
396 | <li class="${is_active('journal')}"> |
|
416 | <li class="${is_active('journal')}"> | |
397 | <a class="menulink" title="${_('Show activity journal')}" href="${h.route_path('journal')}"> |
|
417 | <a class="menulink" title="${_('Show activity journal')}" href="${h.route_path('journal')}"> | |
398 | <div class="menulabel">${_('Journal')}</div> |
|
418 | <div class="menulabel">${_('Journal')}</div> | |
399 | </a> |
|
419 | </a> | |
400 | </li> |
|
420 | </li> | |
401 | %else: |
|
421 | %else: | |
402 | <li class="${is_active('journal')}"> |
|
422 | <li class="${is_active('journal')}"> | |
403 | <a class="menulink" title="${_('Show Public activity journal')}" href="${h.route_path('journal_public')}"> |
|
423 | <a class="menulink" title="${_('Show Public activity journal')}" href="${h.route_path('journal_public')}"> | |
404 | <div class="menulabel">${_('Public journal')}</div> |
|
424 | <div class="menulabel">${_('Public journal')}</div> | |
405 | </a> |
|
425 | </a> | |
406 | </li> |
|
426 | </li> | |
407 | %endif |
|
427 | %endif | |
408 | <li class="${is_active('gists')}"> |
|
428 | <li class="${is_active('gists')}"> | |
409 | <a class="menulink childs" title="${_('Show Gists')}" href="${h.route_path('gists_show')}"> |
|
429 | <a class="menulink childs" title="${_('Show Gists')}" href="${h.route_path('gists_show')}"> | |
410 | <div class="menulabel">${_('Gists')}</div> |
|
430 | <div class="menulabel">${_('Gists')}</div> | |
411 | </a> |
|
431 | </a> | |
412 | </li> |
|
432 | </li> | |
413 | <li class="${is_active('search')}"> |
|
433 | <li class="${is_active('search')}"> | |
414 | <a class="menulink" title="${_('Search in repositories you have access to')}" href="${h.route_path('search')}"> |
|
434 | <a class="menulink" title="${_('Search in repositories you have access to')}" href="${h.route_path('search')}"> | |
415 | <div class="menulabel">${_('Search')}</div> |
|
435 | <div class="menulabel">${_('Search')}</div> | |
416 | </a> |
|
436 | </a> | |
417 | </li> |
|
437 | </li> | |
418 | % if h.HasPermissionAll('hg.admin')('access admin main page'): |
|
438 | % if h.HasPermissionAll('hg.admin')('access admin main page'): | |
419 | <li class="${is_active('admin')}"> |
|
439 | <li class="${is_active('admin')}"> | |
420 | <a class="menulink childs" title="${_('Admin settings')}" href="#" onclick="return false;"> |
|
440 | <a class="menulink childs" title="${_('Admin settings')}" href="#" onclick="return false;"> | |
421 | <div class="menulabel">${_('Admin')} <div class="show_more"></div></div> |
|
441 | <div class="menulabel">${_('Admin')} <div class="show_more"></div></div> | |
422 | </a> |
|
442 | </a> | |
423 | ${admin_menu()} |
|
443 | ${admin_menu()} | |
424 | </li> |
|
444 | </li> | |
425 | % elif c.rhodecode_user.repositories_admin or c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin: |
|
445 | % elif c.rhodecode_user.repositories_admin or c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin: | |
426 | <li class="${is_active('admin')}"> |
|
446 | <li class="${is_active('admin')}"> | |
427 | <a class="menulink childs" title="${_('Delegated Admin settings')}"> |
|
447 | <a class="menulink childs" title="${_('Delegated Admin settings')}"> | |
428 | <div class="menulabel">${_('Admin')} <div class="show_more"></div></div> |
|
448 | <div class="menulabel">${_('Admin')} <div class="show_more"></div></div> | |
429 | </a> |
|
449 | </a> | |
430 | ${admin_menu_simple(c.rhodecode_user.repositories_admin, |
|
450 | ${admin_menu_simple(c.rhodecode_user.repositories_admin, | |
431 | c.rhodecode_user.repository_groups_admin, |
|
451 | c.rhodecode_user.repository_groups_admin, | |
432 | c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())} |
|
452 | c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())} | |
433 | </li> |
|
453 | </li> | |
434 | % endif |
|
454 | % endif | |
|
455 | ## render extra user menu | |||
|
456 | ${usermenu(active=(active=='my_account'))} | |||
|
457 | ||||
435 |
|
|
458 | % if c.debug_style: | |
436 | <li class="${is_active('debug_style')}"> |
|
459 | <li class="${is_active('debug_style')}"> | |
437 | <a class="menulink" title="${_('Style')}" href="${h.route_path('debug_style_home')}"> |
|
460 | <a class="menulink" title="${_('Style')}" href="${h.route_path('debug_style_home')}"> | |
438 | <div class="menulabel">${_('Style')}</div> |
|
461 | <div class="menulabel">${_('Style')}</div> | |
439 | </a> |
|
462 | </a> | |
440 | </li> |
|
463 | </li> | |
441 | % endif |
|
464 | % endif | |
442 | ## render extra user menu |
|
|||
443 | ${usermenu(active=(active=='my_account'))} |
|
|||
444 | </ul> |
|
465 | </ul> | |
445 |
|
466 | |||
446 | <script type="text/javascript"> |
|
467 | <script type="text/javascript"> | |
447 |
var visual |
|
468 | var visualShowPublicIcon = "${c.visual.show_public_icon}" == "True"; | |
448 |
|
469 | |||
449 | /*format the look of items in the list*/ |
|
470 | var formatRepoResult = function(result, container, query, escapeMarkup) { | |
450 |
|
|
471 | return function(data, escapeMarkup) { | |
451 |
if (! |
|
472 | if (!data.repo_id){ | |
452 |
return |
|
473 | return data.text; // optgroup text Repositories | |
453 | } |
|
474 | } | |
454 | var obj_dict = state.obj; |
|
|||
455 | var tmpl = ''; |
|
|||
456 |
|
475 | |||
457 | if(obj_dict && state.type == 'repo'){ |
|
476 | var tmpl = ''; | |
458 |
|
|
477 | var repoType = data['repo_type']; | |
|
478 | var repoName = data['text']; | |||
|
479 | ||||
|
480 | if(data && data.type == 'repo'){ | |||
|
481 | if(repoType === 'hg'){ | |||
459 | tmpl += '<i class="icon-hg"></i> '; |
|
482 | tmpl += '<i class="icon-hg"></i> '; | |
460 | } |
|
483 | } | |
461 |
else if( |
|
484 | else if(repoType === 'git'){ | |
462 | tmpl += '<i class="icon-git"></i> '; |
|
485 | tmpl += '<i class="icon-git"></i> '; | |
463 | } |
|
486 | } | |
464 |
else if( |
|
487 | else if(repoType === 'svn'){ | |
465 | tmpl += '<i class="icon-svn"></i> '; |
|
488 | tmpl += '<i class="icon-svn"></i> '; | |
466 | } |
|
489 | } | |
467 |
if( |
|
490 | if(data['private']){ | |
468 | tmpl += '<i class="icon-lock" ></i> '; |
|
491 | tmpl += '<i class="icon-lock" ></i> '; | |
469 | } |
|
492 | } | |
470 |
else if(visual |
|
493 | else if(visualShowPublicIcon){ | |
471 | tmpl += '<i class="icon-unlock-alt"></i> '; |
|
494 | tmpl += '<i class="icon-unlock-alt"></i> '; | |
472 | } |
|
495 | } | |
473 | } |
|
496 | } | |
474 | if(obj_dict && state.type == 'commit') { |
|
497 | tmpl += escapeMarkup(repoName); | |
475 | tmpl += '<i class="icon-tag"></i>'; |
|
|||
476 | } |
|
|||
477 | if(obj_dict && state.type == 'group'){ |
|
|||
478 | tmpl += '<i class="icon-folder-close"></i> '; |
|
|||
479 | } |
|
|||
480 | tmpl += escapeMarkup(state.text); |
|
|||
481 | return tmpl; |
|
498 | return tmpl; | |
482 | }; |
|
|||
483 |
|
499 | |||
484 | var formatResult = function(result, container, query, escapeMarkup) { |
|
500 | }(result, escapeMarkup); | |
485 | return format(result, escapeMarkup); |
|
|||
486 | }; |
|
|||
487 |
|
||||
488 | var formatSelection = function(data, container, escapeMarkup) { |
|
|||
489 | return format(data, escapeMarkup); |
|
|||
490 | }; |
|
501 | }; | |
491 |
|
502 | |||
492 | $("#repo_switcher").select2({ |
|
503 | ||
493 | cachedDataSource: {}, |
|
504 | var autocompleteMainFilterFormatResult = function (data, value, org_formatter) { | |
494 | minimumInputLength: 2, |
|
505 | ||
495 | placeholder: '<div class="menulabel">${_('Go to')} <div class="show_more"></div></div>', |
|
506 | if (value.split(':').length === 2) { | |
496 | dropdownAutoWidth: true, |
|
507 | value = value.split(':')[1] | |
497 | formatResult: formatResult, |
|
|||
498 | formatSelection: formatSelection, |
|
|||
499 | containerCssClass: "repo-switcher", |
|
|||
500 | dropdownCssClass: "repo-switcher-dropdown", |
|
|||
501 | escapeMarkup: function(m){ |
|
|||
502 | // don't escape our custom placeholder |
|
|||
503 | if(m.substr(0,23) == '<div class="menulabel">'){ |
|
|||
504 | return m; |
|
|||
505 |
|
|
508 | } | |
506 |
|
509 | |||
507 | return Select2.util.escapeMarkup(m); |
|
510 | var searchType = data['type']; | |
508 | }, |
|
511 | var valueDisplay = data['value_display']; | |
509 | query: $.debounce(250, function(query){ |
|
512 | ||
510 | self = this; |
|
513 | var escapeRegExChars = function (value) { | |
511 | var cacheKey = query.term; |
|
514 | return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); | |
512 | var cachedData = self.cachedDataSource[cacheKey]; |
|
515 | }; | |
|
516 | var pattern = '(' + escapeRegExChars(value) + ')'; | |||
|
517 | ||||
|
518 | // highlight match | |||
|
519 | valueDisplay = Select2.util.escapeMarkup(valueDisplay); | |||
|
520 | valueDisplay = valueDisplay.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>'); | |||
|
521 | ||||
|
522 | var icon = ''; | |||
513 |
|
523 | |||
514 |
|
|
524 | if (searchType === 'search') { | |
515 | query.callback({results: cachedData.results}); |
|
525 | icon += '<i class="icon-more"></i> '; | |
516 |
|
|
526 | } | |
517 | $.ajax({ |
|
527 | else if (searchType === 'repo') { | |
518 | url: pyroutes.url('goto_switcher_data'), |
|
528 | if (data['repo_type'] === 'hg') { | |
519 | data: {'query': query.term}, |
|
529 | icon += '<i class="icon-hg"></i> '; | |
520 | dataType: 'json', |
|
530 | } | |
521 | type: 'GET', |
|
531 | else if (data['repo_type'] === 'git') { | |
522 | success: function(data) { |
|
532 | icon += '<i class="icon-git"></i> '; | |
523 | self.cachedDataSource[cacheKey] = data; |
|
533 | } | |
524 | query.callback({results: data.results}); |
|
534 | else if (data['repo_type'] === 'svn') { | |
525 | }, |
|
535 | icon += '<i class="icon-svn"></i> '; | |
526 | error: function(data, textStatus, errorThrown) { |
|
536 | } | |
527 | alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText)); |
|
537 | if (data['private']) { | |
|
538 | icon += '<i class="icon-lock" ></i> '; | |||
|
539 | } | |||
|
540 | else if (visualShowPublicIcon) { | |||
|
541 | icon += '<i class="icon-unlock-alt"></i> '; | |||
|
542 | } | |||
|
543 | } | |||
|
544 | else if (searchType === 'repo_group') { | |||
|
545 | icon += '<i class="icon-folder-close"></i> '; | |||
|
546 | } | |||
|
547 | else if (searchType === 'user') { | |||
|
548 | icon += '<img class="gravatar" src="{0}"/>'.format(data['icon_link']); | |||
|
549 | } | |||
|
550 | else if (searchType === 'commit') { | |||
|
551 | icon += '<i class="icon-tag"></i>'; | |||
528 |
|
|
552 | } | |
529 | }) |
|
553 | ||
|
554 | var tmpl = '<div class="ac-container-wrap">{0}{1}</div>'; | |||
|
555 | return tmpl.format(icon, valueDisplay); | |||
|
556 | }; | |||
|
557 | ||||
|
558 | var handleSelect = function(element, suggestion) { | |||
|
559 | window.location = suggestion['url']; | |||
|
560 | }; | |||
|
561 | var autocompleteMainFilterResult = function (suggestion, originalQuery, queryLowerCase) { | |||
|
562 | if (queryLowerCase.split(':').length === 2) { | |||
|
563 | queryLowerCase = queryLowerCase.split(':')[1] | |||
530 |
|
|
564 | } | |
531 | }) |
|
565 | return suggestion.value_display.toLowerCase().indexOf(queryLowerCase) !== -1; | |
|
566 | }; | |||
|
567 | ||||
|
568 | $('#main_filter').autocomplete({ | |||
|
569 | serviceUrl: pyroutes.url('goto_switcher_data'), | |||
|
570 | minChars:2, | |||
|
571 | maxHeight:400, | |||
|
572 | deferRequestBy: 300, //miliseconds | |||
|
573 | tabDisabled: true, | |||
|
574 | autoSelectFirst: true, | |||
|
575 | formatResult: autocompleteMainFilterFormatResult, | |||
|
576 | lookupFilter: autocompleteMainFilterResult, | |||
|
577 | onSelect: function(element, suggestion){ | |||
|
578 | handleSelect(element, suggestion); | |||
|
579 | return false; | |||
|
580 | } | |||
532 | }); |
|
581 | }); | |
533 |
|
582 | |||
534 | $("#repo_switcher").on('select2-selecting', function(e){ |
|
583 | showMainFilterBox = function () { | |
535 | e.preventDefault(); |
|
584 | $('#main_filter_help').toggle(); | |
536 | window.location = e.choice.url; |
|
585 | } | |
537 | }); |
|
|||
538 |
|
586 | |||
539 | </script> |
|
587 | </script> | |
540 | <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script> |
|
588 | <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script> | |
541 | </%def> |
|
589 | </%def> | |
542 |
|
590 | |||
543 | <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> |
|
591 | <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> | |
544 | <div class="modal-dialog"> |
|
592 | <div class="modal-dialog"> | |
545 | <div class="modal-content"> |
|
593 | <div class="modal-content"> | |
546 | <div class="modal-header"> |
|
594 | <div class="modal-header"> | |
547 | <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> |
|
595 | <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> | |
548 | <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4> |
|
596 | <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4> | |
549 | </div> |
|
597 | </div> | |
550 | <div class="modal-body"> |
|
598 | <div class="modal-body"> | |
551 | <div class="block-left"> |
|
599 | <div class="block-left"> | |
552 | <table class="keyboard-mappings"> |
|
600 | <table class="keyboard-mappings"> | |
553 | <tbody> |
|
601 | <tbody> | |
554 | <tr> |
|
602 | <tr> | |
555 | <th></th> |
|
603 | <th></th> | |
556 | <th>${_('Site-wide shortcuts')}</th> |
|
604 | <th>${_('Site-wide shortcuts')}</th> | |
557 | </tr> |
|
605 | </tr> | |
558 | <% |
|
606 | <% | |
559 | elems = [ |
|
607 | elems = [ | |
560 |
('/', ' |
|
608 | ('/', 'Use quick search box'), | |
561 | ('g h', 'Goto home page'), |
|
609 | ('g h', 'Goto home page'), | |
562 | ('g g', 'Goto my private gists page'), |
|
610 | ('g g', 'Goto my private gists page'), | |
563 | ('g G', 'Goto my public gists page'), |
|
611 | ('g G', 'Goto my public gists page'), | |
564 | ('n r', 'New repository page'), |
|
612 | ('n r', 'New repository page'), | |
565 | ('n g', 'New gist page'), |
|
613 | ('n g', 'New gist page'), | |
566 | ] |
|
614 | ] | |
567 | %> |
|
615 | %> | |
568 | %for key, desc in elems: |
|
616 | %for key, desc in elems: | |
569 | <tr> |
|
617 | <tr> | |
570 | <td class="keys"> |
|
618 | <td class="keys"> | |
571 | <span class="key tag">${key}</span> |
|
619 | <span class="key tag">${key}</span> | |
572 | </td> |
|
620 | </td> | |
573 | <td>${desc}</td> |
|
621 | <td>${desc}</td> | |
574 | </tr> |
|
622 | </tr> | |
575 | %endfor |
|
623 | %endfor | |
576 | </tbody> |
|
624 | </tbody> | |
577 | </table> |
|
625 | </table> | |
578 | </div> |
|
626 | </div> | |
579 | <div class="block-left"> |
|
627 | <div class="block-left"> | |
580 | <table class="keyboard-mappings"> |
|
628 | <table class="keyboard-mappings"> | |
581 | <tbody> |
|
629 | <tbody> | |
582 | <tr> |
|
630 | <tr> | |
583 | <th></th> |
|
631 | <th></th> | |
584 | <th>${_('Repositories')}</th> |
|
632 | <th>${_('Repositories')}</th> | |
585 | </tr> |
|
633 | </tr> | |
586 | <% |
|
634 | <% | |
587 | elems = [ |
|
635 | elems = [ | |
588 | ('g s', 'Goto summary page'), |
|
636 | ('g s', 'Goto summary page'), | |
589 | ('g c', 'Goto changelog page'), |
|
637 | ('g c', 'Goto changelog page'), | |
590 | ('g f', 'Goto files page'), |
|
638 | ('g f', 'Goto files page'), | |
591 | ('g F', 'Goto files page with file search activated'), |
|
639 | ('g F', 'Goto files page with file search activated'), | |
592 | ('g p', 'Goto pull requests page'), |
|
640 | ('g p', 'Goto pull requests page'), | |
593 | ('g o', 'Goto repository settings'), |
|
641 | ('g o', 'Goto repository settings'), | |
594 | ('g O', 'Goto repository permissions settings'), |
|
642 | ('g O', 'Goto repository permissions settings'), | |
595 | ] |
|
643 | ] | |
596 | %> |
|
644 | %> | |
597 | %for key, desc in elems: |
|
645 | %for key, desc in elems: | |
598 | <tr> |
|
646 | <tr> | |
599 | <td class="keys"> |
|
647 | <td class="keys"> | |
600 | <span class="key tag">${key}</span> |
|
648 | <span class="key tag">${key}</span> | |
601 | </td> |
|
649 | </td> | |
602 | <td>${desc}</td> |
|
650 | <td>${desc}</td> | |
603 | </tr> |
|
651 | </tr> | |
604 | %endfor |
|
652 | %endfor | |
605 | </tbody> |
|
653 | </tbody> | |
606 | </table> |
|
654 | </table> | |
607 | </div> |
|
655 | </div> | |
608 | </div> |
|
656 | </div> | |
609 | <div class="modal-footer"> |
|
657 | <div class="modal-footer"> | |
610 | </div> |
|
658 | </div> | |
611 | </div><!-- /.modal-content --> |
|
659 | </div><!-- /.modal-content --> | |
612 | </div><!-- /.modal-dialog --> |
|
660 | </div><!-- /.modal-dialog --> | |
613 | </div><!-- /.modal --> |
|
661 | </div><!-- /.modal --> | |
|
662 |
@@ -1,201 +1,141 b'' | |||||
1 | <%inherit file="/base/base.mako"/> |
|
1 | <%inherit file="/base/base.mako"/> | |
2 |
|
2 | |||
3 | <%def name="main()"> |
|
3 | <%def name="main()"> | |
4 | <div class="box"> |
|
4 | <div class="box"> | |
5 | <!-- box / title --> |
|
5 | <!-- box / title --> | |
6 | <div class="title"> |
|
6 | <div class="title"> | |
7 | <div class="block-left breadcrumbs"> |
|
7 | <div class="block-left breadcrumbs"> | |
8 | <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/> |
|
|||
9 | ${self.breadcrumbs()} |
|
8 | ${self.breadcrumbs()} | |
10 |
<span id="match_container" style="display:none"> |
|
9 | <span id="match_container" style="display:none"><span id="match_count">0</span> ${_('matches')}</span> | |
11 | </div> |
|
10 | </div> | |
12 | %if c.rhodecode_user.username != h.DEFAULT_USER: |
|
11 | %if c.rhodecode_user.username != h.DEFAULT_USER: | |
13 | <div class="block-right"> |
|
12 | <div class="block-right"> | |
14 | <% |
|
13 | <% | |
15 | is_admin = h.HasPermissionAny('hg.admin')('can create repos index page') |
|
14 | is_admin = h.HasPermissionAny('hg.admin')('can create repos index page') | |
16 | create_repo = h.HasPermissionAny('hg.create.repository')('can create repository index page') |
|
15 | create_repo = h.HasPermissionAny('hg.create.repository')('can create repository index page') | |
17 | create_repo_group = h.HasPermissionAny('hg.repogroup.create.true')('can create repository groups index page') |
|
16 | create_repo_group = h.HasPermissionAny('hg.repogroup.create.true')('can create repository groups index page') | |
18 | create_user_group = h.HasPermissionAny('hg.usergroup.create.true')('can create user groups index page') |
|
17 | create_user_group = h.HasPermissionAny('hg.usergroup.create.true')('can create user groups index page') | |
19 |
|
18 | |||
20 | gr_name = c.repo_group.group_name if c.repo_group else None |
|
19 | gr_name = c.repo_group.group_name if c.repo_group else None | |
21 | # create repositories with write permission on group is set to true |
|
20 | # create repositories with write permission on group is set to true | |
22 | create_on_write = h.HasPermissionAny('hg.create.write_on_repogroup.true')() |
|
21 | create_on_write = h.HasPermissionAny('hg.create.write_on_repogroup.true')() | |
23 | group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page') |
|
22 | group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page') | |
24 | group_write = h.HasRepoGroupPermissionAny('group.write')(gr_name, 'can write into group index page') |
|
23 | group_write = h.HasRepoGroupPermissionAny('group.write')(gr_name, 'can write into group index page') | |
25 | %> |
|
24 | %> | |
26 |
|
25 | |||
27 | %if not c.repo_group: |
|
26 | %if not c.repo_group: | |
28 | ## no repository group context here |
|
27 | ## no repository group context here | |
29 | %if is_admin or create_repo: |
|
28 | %if is_admin or create_repo: | |
30 | <a href="${h.route_path('repo_new')}" class="btn btn-small btn-success btn-primary">${_('Add Repository')}</a> |
|
29 | <a href="${h.route_path('repo_new')}" class="btn btn-small btn-success btn-primary">${_('Add Repository')}</a> | |
31 | %endif |
|
30 | %endif | |
32 |
|
31 | |||
33 | %if is_admin or create_repo_group: |
|
32 | %if is_admin or create_repo_group: | |
34 | <a href="${h.route_path('repo_group_new')}" class="btn btn-small btn-default">${_(u'Add Repository Group')}</a> |
|
33 | <a href="${h.route_path('repo_group_new')}" class="btn btn-small btn-default">${_(u'Add Repository Group')}</a> | |
35 | %endif |
|
34 | %endif | |
36 | %else: |
|
35 | %else: | |
37 | ##we're inside other repository group other terms apply |
|
36 | ##we're inside other repository group other terms apply | |
38 | %if is_admin or group_admin or (group_write and create_on_write): |
|
37 | %if is_admin or group_admin or (group_write and create_on_write): | |
39 | <a href="${h.route_path('repo_new',_query=dict(parent_group=c.repo_group.group_id))}" class="btn btn-small btn-success btn-primary">${_('Add Repository')}</a> |
|
38 | <a href="${h.route_path('repo_new',_query=dict(parent_group=c.repo_group.group_id))}" class="btn btn-small btn-success btn-primary">${_('Add Repository')}</a> | |
40 | %endif |
|
39 | %endif | |
41 | %if is_admin or group_admin: |
|
40 | %if is_admin or group_admin: | |
42 | <a href="${h.route_path('repo_group_new',_query=dict(parent_group=c.repo_group.group_id))}" class="btn btn-small btn-default">${_(u'Add Repository Group')}</a> |
|
41 | <a href="${h.route_path('repo_group_new',_query=dict(parent_group=c.repo_group.group_id))}" class="btn btn-small btn-default">${_(u'Add Repository Group')}</a> | |
43 | %endif |
|
42 | %endif | |
44 | %if is_admin or group_admin: |
|
43 | %if is_admin or group_admin: | |
45 | <a href="${h.route_path('edit_repo_group',repo_group_name=c.repo_group.group_name)}" title="${_('You have admin right to this group, and can edit it')}" class="btn btn-small btn-primary">${_('Edit Repository Group')}</a> |
|
44 | <a href="${h.route_path('edit_repo_group',repo_group_name=c.repo_group.group_name)}" title="${_('You have admin right to this group, and can edit it')}" class="btn btn-small btn-primary">${_('Edit Repository Group')}</a> | |
46 | %endif |
|
45 | %endif | |
47 | %endif |
|
46 | %endif | |
48 | </div> |
|
47 | </div> | |
49 | %endif |
|
48 | %endif | |
50 | </div> |
|
49 | </div> | |
51 | <!-- end box / title --> |
|
50 | <!-- end box / title --> | |
52 | <div class="table"> |
|
51 | <div class="table"> | |
53 | <div id="groups_list_wrap"> |
|
52 | <div id="groups_list_wrap"> | |
54 | <table id="group_list_table" class="display"></table> |
|
53 | <table id="group_list_table" class="display"></table> | |
55 | </div> |
|
54 | </div> | |
56 | </div> |
|
55 | </div> | |
57 |
|
56 | |||
58 | <div class="table"> |
|
57 | <div class="table"> | |
59 | <div id="repos_list_wrap"> |
|
58 | <div id="repos_list_wrap"> | |
60 | <table id="repo_list_table" class="display"></table> |
|
59 | <table id="repo_list_table" class="display"></table> | |
61 | </div> |
|
60 | </div> | |
62 | </div> |
|
61 | </div> | |
63 |
|
62 | |||
64 | ## no repository groups and repos present, show something to the users |
|
63 | ## no repository groups and repos present, show something to the users | |
65 | % if c.repo_groups_data == '[]' and c.repos_data == '[]': |
|
64 | % if c.repo_groups_data == '[]' and c.repos_data == '[]': | |
66 | <div class="table"> |
|
65 | <div class="table"> | |
67 | <h2 class="no-object-border"> |
|
66 | <h2 class="no-object-border"> | |
68 | ${_('No repositories or repositories groups exists here.')} |
|
67 | ${_('No repositories or repositories groups exists here.')} | |
69 | </h2> |
|
68 | </h2> | |
70 | </div> |
|
69 | </div> | |
71 | % endif |
|
70 | % endif | |
72 |
|
71 | |||
73 | </div> |
|
72 | </div> | |
74 | <script> |
|
73 | <script> | |
75 | $(document).ready(function() { |
|
74 | $(document).ready(function() { | |
76 |
|
75 | |||
77 | // repo group list |
|
76 | // repo group list | |
78 | % if c.repo_groups_data != '[]': |
|
77 | % if c.repo_groups_data != '[]': | |
79 | $('#group_list_table').DataTable({ |
|
78 | $('#group_list_table').DataTable({ | |
80 | data: ${c.repo_groups_data|n}, |
|
79 | data: ${c.repo_groups_data|n}, | |
81 | dom: 'rtp', |
|
80 | dom: 'rtp', | |
82 | pageLength: ${c.visual.dashboard_items}, |
|
81 | pageLength: ${c.visual.dashboard_items}, | |
83 | order: [[ 0, "asc" ]], |
|
82 | order: [[ 0, "asc" ]], | |
84 | columns: [ |
|
83 | columns: [ | |
85 | { data: {"_": "name", |
|
84 | { data: {"_": "name", | |
86 | "sort": "name_raw"}, title: "${_('Name')}", className: "td-componentname" }, |
|
85 | "sort": "name_raw"}, title: "${_('Name')}", className: "td-componentname" }, | |
87 | { data: 'menu', "bSortable": false, className: "quick_repo_menu" }, |
|
86 | { data: 'menu', "bSortable": false, className: "quick_repo_menu" }, | |
88 | { data: {"_": "desc", |
|
87 | { data: {"_": "desc", | |
89 | "sort": "desc"}, title: "${_('Description')}", className: "td-description" }, |
|
88 | "sort": "desc"}, title: "${_('Description')}", className: "td-description" }, | |
90 | { data: {"_": "last_change", |
|
89 | { data: {"_": "last_change", | |
91 | "sort": "last_change_raw", |
|
90 | "sort": "last_change_raw", | |
92 | "type": Number}, title: "${_('Last Change')}", className: "td-time" }, |
|
91 | "type": Number}, title: "${_('Last Change')}", className: "td-time" }, | |
93 | { data: {"_": "owner", |
|
92 | { data: {"_": "owner", | |
94 | "sort": "owner"}, title: "${_('Owner')}", className: "td-user" } |
|
93 | "sort": "owner"}, title: "${_('Owner')}", className: "td-user" } | |
95 | ], |
|
94 | ], | |
96 | language: { |
|
95 | language: { | |
97 | paginate: DEFAULT_GRID_PAGINATION, |
|
96 | paginate: DEFAULT_GRID_PAGINATION, | |
98 | emptyTable: _gettext("No repository groups available yet.") |
|
97 | emptyTable: _gettext("No repository groups available yet.") | |
99 | }, |
|
98 | }, | |
100 | "drawCallback": function( settings, json ) { |
|
99 | "drawCallback": function( settings, json ) { | |
101 | timeagoActivate(); |
|
100 | timeagoActivate(); | |
102 | quick_repo_menu(); |
|
101 | quick_repo_menu(); | |
103 | } |
|
102 | } | |
104 | }); |
|
103 | }); | |
105 | % endif |
|
104 | % endif | |
106 |
|
105 | |||
107 | // repo list |
|
106 | // repo list | |
108 | % if c.repos_data != '[]': |
|
107 | % if c.repos_data != '[]': | |
109 | $('#repo_list_table').DataTable({ |
|
108 | $('#repo_list_table').DataTable({ | |
110 | data: ${c.repos_data|n}, |
|
109 | data: ${c.repos_data|n}, | |
111 | dom: 'rtp', |
|
110 | dom: 'rtp', | |
112 | order: [[ 0, "asc" ]], |
|
111 | order: [[ 0, "asc" ]], | |
113 | pageLength: ${c.visual.dashboard_items}, |
|
112 | pageLength: ${c.visual.dashboard_items}, | |
114 | columns: [ |
|
113 | columns: [ | |
115 | { data: {"_": "name", |
|
114 | { data: {"_": "name", | |
116 | "sort": "name_raw"}, title: "${_('Name')}", className: "truncate-wrap td-componentname" }, |
|
115 | "sort": "name_raw"}, title: "${_('Name')}", className: "truncate-wrap td-componentname" }, | |
117 | { data: 'menu', "bSortable": false, className: "quick_repo_menu" }, |
|
116 | { data: 'menu', "bSortable": false, className: "quick_repo_menu" }, | |
118 | { data: {"_": "desc", |
|
117 | { data: {"_": "desc", | |
119 | "sort": "desc"}, title: "${_('Description')}", className: "td-description" }, |
|
118 | "sort": "desc"}, title: "${_('Description')}", className: "td-description" }, | |
120 | { data: {"_": "last_change", |
|
119 | { data: {"_": "last_change", | |
121 | "sort": "last_change_raw", |
|
120 | "sort": "last_change_raw", | |
122 | "type": Number}, title: "${_('Last Change')}", className: "td-time" }, |
|
121 | "type": Number}, title: "${_('Last Change')}", className: "td-time" }, | |
123 | { data: {"_": "last_changeset", |
|
122 | { data: {"_": "last_changeset", | |
124 | "sort": "last_changeset_raw", |
|
123 | "sort": "last_changeset_raw", | |
125 | "type": Number}, title: "${_('Commit')}", className: "td-hash" }, |
|
124 | "type": Number}, title: "${_('Commit')}", className: "td-hash" }, | |
126 | { data: {"_": "owner", |
|
125 | { data: {"_": "owner", | |
127 | "sort": "owner"}, title: "${_('Owner')}", className: "td-user" } |
|
126 | "sort": "owner"}, title: "${_('Owner')}", className: "td-user" } | |
128 | ], |
|
127 | ], | |
129 | language: { |
|
128 | language: { | |
130 | paginate: DEFAULT_GRID_PAGINATION, |
|
129 | paginate: DEFAULT_GRID_PAGINATION, | |
131 | emptyTable: _gettext("No repositories available yet.") |
|
130 | emptyTable: _gettext("No repositories available yet.") | |
132 | }, |
|
131 | }, | |
133 | "drawCallback": function( settings, json ) { |
|
132 | "drawCallback": function( settings, json ) { | |
134 | timeagoActivate(); |
|
133 | timeagoActivate(); | |
135 | quick_repo_menu(); |
|
134 | quick_repo_menu(); | |
136 | } |
|
135 | } | |
137 | }); |
|
136 | }); | |
138 | % endif |
|
137 | % endif | |
139 |
|
138 | |||
140 | var getDatatableCount = function() { |
|
|||
141 | var reposCount = 0; |
|
|||
142 | var reposCountTotal = 0; |
|
|||
143 |
|
||||
144 | % if c.repos_data != '[]': |
|
|||
145 | var pageInfo = $('#repo_list_table').dataTable().api().page.info(); |
|
|||
146 | var reposCount = pageInfo.recordsDisplay; |
|
|||
147 | var reposCountTotal = pageInfo.recordsTotal; |
|
|||
148 | % endif |
|
|||
149 |
|
||||
150 | var repoGroupsCount = 0; |
|
|||
151 | var repoGroupsCountTotal = 0; |
|
|||
152 |
|
||||
153 | % if c.repo_groups_data != '[]': |
|
|||
154 | var pageInfo = $('#group_list_table').dataTable().api().page.info(); |
|
|||
155 | var repoGroupsCount = pageInfo.recordsDisplay; |
|
|||
156 | var repoGroupsCountTotal = pageInfo.recordsTotal; |
|
|||
157 | % endif |
|
|||
158 |
|
||||
159 | if (repoGroupsCount !== repoGroupsCountTotal) { |
|
|||
160 | $('#match_count').text(reposCount + repoGroupsCount); |
|
|||
161 | } |
|
|||
162 | if (reposCount !== reposCountTotal) { |
|
|||
163 | $('#match_container').show(); |
|
|||
164 | } |
|
|||
165 | if ($('#q_filter').val() === '') { |
|
|||
166 | $('#match_container').hide(); |
|
|||
167 | } |
|
|||
168 | }; |
|
|||
169 |
|
||||
170 | // update the counter when doing search |
|
|||
171 | $('#repo_list_table, #group_list_table').on( 'search.dt', function (e,settings) { |
|
|||
172 | getDatatableCount(); |
|
|||
173 | }); |
|
|||
174 |
|
||||
175 | // filter, filter both grids |
|
|||
176 | $('#q_filter').on( 'keyup', function () { |
|
|||
177 |
|
||||
178 | % if c.repo_groups_data != '[]': |
|
|||
179 | var repo_group_api = $('#group_list_table').dataTable().api(); |
|
|||
180 | repo_group_api |
|
|||
181 | .columns( 0 ) |
|
|||
182 | .search( this.value ) |
|
|||
183 | .draw(); |
|
|||
184 | % endif |
|
|||
185 |
|
||||
186 | % if c.repos_data != '[]': |
|
|||
187 | var repo_api = $('#repo_list_table').dataTable().api(); |
|
|||
188 | repo_api |
|
|||
189 | .columns( 0 ) |
|
|||
190 | .search( this.value ) |
|
|||
191 | .draw(); |
|
|||
192 | % endif |
|
|||
193 |
|
||||
194 | }); |
|
|||
195 |
|
||||
196 | // refilter table if page load via back button |
|
|||
197 | $("#q_filter").trigger('keyup'); |
|
|||
198 |
|
||||
199 | }); |
|
139 | }); | |
200 | </script> |
|
140 | </script> | |
201 | </%def> |
|
141 | </%def> |
@@ -1,537 +1,537 b'' | |||||
1 | <%inherit file="/base/base.mako"/> |
|
1 | <%inherit file="/base/base.mako"/> | |
2 |
|
2 | |||
3 | <%def name="title()"> |
|
3 | <%def name="title()"> | |
4 | ${c.repo_name} ${_('New pull request')} |
|
4 | ${c.repo_name} ${_('New pull request')} | |
5 | </%def> |
|
5 | </%def> | |
6 |
|
6 | |||
7 | <%def name="breadcrumbs_links()"> |
|
7 | <%def name="breadcrumbs_links()"> | |
8 | ${_('New pull request')} |
|
8 | ${_('New pull request')} | |
9 | </%def> |
|
9 | </%def> | |
10 |
|
10 | |||
11 | <%def name="menu_bar_nav()"> |
|
11 | <%def name="menu_bar_nav()"> | |
12 | ${self.menu_items(active='repositories')} |
|
12 | ${self.menu_items(active='repositories')} | |
13 | </%def> |
|
13 | </%def> | |
14 |
|
14 | |||
15 | <%def name="menu_bar_subnav()"> |
|
15 | <%def name="menu_bar_subnav()"> | |
16 | ${self.repo_menu(active='showpullrequest')} |
|
16 | ${self.repo_menu(active='showpullrequest')} | |
17 | </%def> |
|
17 | </%def> | |
18 |
|
18 | |||
19 | <%def name="main()"> |
|
19 | <%def name="main()"> | |
20 | <div class="box"> |
|
20 | <div class="box"> | |
21 | <div class="title"> |
|
21 | <div class="title"> | |
22 | ${self.repo_page_title(c.rhodecode_db_repo)} |
|
22 | ${self.repo_page_title(c.rhodecode_db_repo)} | |
23 | </div> |
|
23 | </div> | |
24 |
|
24 | |||
25 | ${h.secure_form(h.route_path('pullrequest_create', repo_name=c.repo_name, _query=request.GET.mixed()), id='pull_request_form', request=request)} |
|
25 | ${h.secure_form(h.route_path('pullrequest_create', repo_name=c.repo_name, _query=request.GET.mixed()), id='pull_request_form', request=request)} | |
26 |
|
26 | |||
27 | ${self.breadcrumbs()} |
|
27 | ${self.breadcrumbs()} | |
28 |
|
28 | |||
29 | <div class="box pr-summary"> |
|
29 | <div class="box pr-summary"> | |
30 |
|
30 | |||
31 | <div class="summary-details block-left"> |
|
31 | <div class="summary-details block-left"> | |
32 |
|
32 | |||
33 |
|
33 | |||
34 | <div class="pr-details-title"> |
|
34 | <div class="pr-details-title"> | |
35 | ${_('Pull request summary')} |
|
35 | ${_('Pull request summary')} | |
36 | </div> |
|
36 | </div> | |
37 |
|
37 | |||
38 | <div class="form" style="padding-top: 10px"> |
|
38 | <div class="form" style="padding-top: 10px"> | |
39 | <!-- fields --> |
|
39 | <!-- fields --> | |
40 |
|
40 | |||
41 | <div class="fields" > |
|
41 | <div class="fields" > | |
42 |
|
42 | |||
43 | <div class="field"> |
|
43 | <div class="field"> | |
44 | <div class="label"> |
|
44 | <div class="label"> | |
45 | <label for="pullrequest_title">${_('Title')}:</label> |
|
45 | <label for="pullrequest_title">${_('Title')}:</label> | |
46 | </div> |
|
46 | </div> | |
47 | <div class="input"> |
|
47 | <div class="input"> | |
48 | ${h.text('pullrequest_title', c.default_title, class_="medium autogenerated-title")} |
|
48 | ${h.text('pullrequest_title', c.default_title, class_="medium autogenerated-title")} | |
49 | </div> |
|
49 | </div> | |
50 | </div> |
|
50 | </div> | |
51 |
|
51 | |||
52 | <div class="field"> |
|
52 | <div class="field"> | |
53 | <div class="label label-textarea"> |
|
53 | <div class="label label-textarea"> | |
54 | <label for="pullrequest_desc">${_('Description')}:</label> |
|
54 | <label for="pullrequest_desc">${_('Description')}:</label> | |
55 | </div> |
|
55 | </div> | |
56 | <div class="textarea text-area editor"> |
|
56 | <div class="textarea text-area editor"> | |
57 | ${h.textarea('pullrequest_desc',size=30, )} |
|
57 | ${h.textarea('pullrequest_desc',size=30, )} | |
58 | <span class="help-block">${_('Write a short description on this pull request')}</span> |
|
58 | <span class="help-block">${_('Write a short description on this pull request')}</span> | |
59 | </div> |
|
59 | </div> | |
60 | </div> |
|
60 | </div> | |
61 |
|
61 | |||
62 | <div class="field"> |
|
62 | <div class="field"> | |
63 | <div class="label label-textarea"> |
|
63 | <div class="label label-textarea"> | |
64 | <label for="pullrequest_desc">${_('Commit flow')}:</label> |
|
64 | <label for="pullrequest_desc">${_('Commit flow')}:</label> | |
65 | </div> |
|
65 | </div> | |
66 |
|
66 | |||
67 | ## TODO: johbo: Abusing the "content" class here to get the |
|
67 | ## TODO: johbo: Abusing the "content" class here to get the | |
68 | ## desired effect. Should be replaced by a proper solution. |
|
68 | ## desired effect. Should be replaced by a proper solution. | |
69 |
|
69 | |||
70 | ##ORG |
|
70 | ##ORG | |
71 | <div class="content"> |
|
71 | <div class="content"> | |
72 | <strong>${_('Source repository')}:</strong> |
|
72 | <strong>${_('Source repository')}:</strong> | |
73 | ${c.rhodecode_db_repo.description} |
|
73 | ${c.rhodecode_db_repo.description} | |
74 | </div> |
|
74 | </div> | |
75 | <div class="content"> |
|
75 | <div class="content"> | |
76 | ${h.hidden('source_repo')} |
|
76 | ${h.hidden('source_repo')} | |
77 | ${h.hidden('source_ref')} |
|
77 | ${h.hidden('source_ref')} | |
78 | </div> |
|
78 | </div> | |
79 |
|
79 | |||
80 | ##OTHER, most Probably the PARENT OF THIS FORK |
|
80 | ##OTHER, most Probably the PARENT OF THIS FORK | |
81 | <div class="content"> |
|
81 | <div class="content"> | |
82 | ## filled with JS |
|
82 | ## filled with JS | |
83 | <div id="target_repo_desc"></div> |
|
83 | <div id="target_repo_desc"></div> | |
84 | </div> |
|
84 | </div> | |
85 |
|
85 | |||
86 | <div class="content"> |
|
86 | <div class="content"> | |
87 | ${h.hidden('target_repo')} |
|
87 | ${h.hidden('target_repo')} | |
88 | ${h.hidden('target_ref')} |
|
88 | ${h.hidden('target_ref')} | |
89 | <span id="target_ref_loading" style="display: none"> |
|
89 | <span id="target_ref_loading" style="display: none"> | |
90 | ${_('Loading refs...')} |
|
90 | ${_('Loading refs...')} | |
91 | </span> |
|
91 | </span> | |
92 | </div> |
|
92 | </div> | |
93 | </div> |
|
93 | </div> | |
94 |
|
94 | |||
95 | <div class="field"> |
|
95 | <div class="field"> | |
96 | <div class="label label-textarea"> |
|
96 | <div class="label label-textarea"> | |
97 | <label for="pullrequest_submit"></label> |
|
97 | <label for="pullrequest_submit"></label> | |
98 | </div> |
|
98 | </div> | |
99 | <div class="input"> |
|
99 | <div class="input"> | |
100 | <div class="pr-submit-button"> |
|
100 | <div class="pr-submit-button"> | |
101 | ${h.submit('save',_('Submit Pull Request'),class_="btn")} |
|
101 | ${h.submit('save',_('Submit Pull Request'),class_="btn")} | |
102 | </div> |
|
102 | </div> | |
103 | <div id="pr_open_message"></div> |
|
103 | <div id="pr_open_message"></div> | |
104 | </div> |
|
104 | </div> | |
105 | </div> |
|
105 | </div> | |
106 |
|
106 | |||
107 | <div class="pr-spacing-container"></div> |
|
107 | <div class="pr-spacing-container"></div> | |
108 | </div> |
|
108 | </div> | |
109 | </div> |
|
109 | </div> | |
110 | </div> |
|
110 | </div> | |
111 | <div> |
|
111 | <div> | |
112 | ## AUTHOR |
|
112 | ## AUTHOR | |
113 | <div class="reviewers-title block-right"> |
|
113 | <div class="reviewers-title block-right"> | |
114 | <div class="pr-details-title"> |
|
114 | <div class="pr-details-title"> | |
115 | ${_('Author of this pull request')} |
|
115 | ${_('Author of this pull request')} | |
116 | </div> |
|
116 | </div> | |
117 | </div> |
|
117 | </div> | |
118 | <div class="block-right pr-details-content reviewers"> |
|
118 | <div class="block-right pr-details-content reviewers"> | |
119 | <ul class="group_members"> |
|
119 | <ul class="group_members"> | |
120 | <li> |
|
120 | <li> | |
121 | ${self.gravatar_with_user(c.rhodecode_user.email, 16)} |
|
121 | ${self.gravatar_with_user(c.rhodecode_user.email, 16)} | |
122 | </li> |
|
122 | </li> | |
123 | </ul> |
|
123 | </ul> | |
124 | </div> |
|
124 | </div> | |
125 |
|
125 | |||
126 | ## REVIEW RULES |
|
126 | ## REVIEW RULES | |
127 | <div id="review_rules" style="display: none" class="reviewers-title block-right"> |
|
127 | <div id="review_rules" style="display: none" class="reviewers-title block-right"> | |
128 | <div class="pr-details-title"> |
|
128 | <div class="pr-details-title"> | |
129 | ${_('Reviewer rules')} |
|
129 | ${_('Reviewer rules')} | |
130 | </div> |
|
130 | </div> | |
131 | <div class="pr-reviewer-rules"> |
|
131 | <div class="pr-reviewer-rules"> | |
132 | ## review rules will be appended here, by default reviewers logic |
|
132 | ## review rules will be appended here, by default reviewers logic | |
133 | </div> |
|
133 | </div> | |
134 | </div> |
|
134 | </div> | |
135 |
|
135 | |||
136 | ## REVIEWERS |
|
136 | ## REVIEWERS | |
137 | <div class="reviewers-title block-right"> |
|
137 | <div class="reviewers-title block-right"> | |
138 | <div class="pr-details-title"> |
|
138 | <div class="pr-details-title"> | |
139 | ${_('Pull request reviewers')} |
|
139 | ${_('Pull request reviewers')} | |
140 | <span class="calculate-reviewers"> - ${_('loading...')}</span> |
|
140 | <span class="calculate-reviewers"> - ${_('loading...')}</span> | |
141 | </div> |
|
141 | </div> | |
142 | </div> |
|
142 | </div> | |
143 | <div id="reviewers" class="block-right pr-details-content reviewers"> |
|
143 | <div id="reviewers" class="block-right pr-details-content reviewers"> | |
144 | ## members goes here, filled via JS based on initial selection ! |
|
144 | ## members goes here, filled via JS based on initial selection ! | |
145 | <input type="hidden" name="__start__" value="review_members:sequence"> |
|
145 | <input type="hidden" name="__start__" value="review_members:sequence"> | |
146 | <ul id="review_members" class="group_members"></ul> |
|
146 | <ul id="review_members" class="group_members"></ul> | |
147 | <input type="hidden" name="__end__" value="review_members:sequence"> |
|
147 | <input type="hidden" name="__end__" value="review_members:sequence"> | |
148 | <div id="add_reviewer_input" class='ac'> |
|
148 | <div id="add_reviewer_input" class='ac'> | |
149 | <div class="reviewer_ac"> |
|
149 | <div class="reviewer_ac"> | |
150 | ${h.text('user', class_='ac-input', placeholder=_('Add reviewer or reviewer group'))} |
|
150 | ${h.text('user', class_='ac-input', placeholder=_('Add reviewer or reviewer group'))} | |
151 | <div id="reviewers_container"></div> |
|
151 | <div id="reviewers_container"></div> | |
152 | </div> |
|
152 | </div> | |
153 | </div> |
|
153 | </div> | |
154 | </div> |
|
154 | </div> | |
155 | </div> |
|
155 | </div> | |
156 | </div> |
|
156 | </div> | |
157 | <div class="box"> |
|
157 | <div class="box"> | |
158 | <div> |
|
158 | <div> | |
159 | ## overview pulled by ajax |
|
159 | ## overview pulled by ajax | |
160 | <div id="pull_request_overview"></div> |
|
160 | <div id="pull_request_overview"></div> | |
161 | </div> |
|
161 | </div> | |
162 | </div> |
|
162 | </div> | |
163 | ${h.end_form()} |
|
163 | ${h.end_form()} | |
164 | </div> |
|
164 | </div> | |
165 |
|
165 | |||
166 | <script type="text/javascript"> |
|
166 | <script type="text/javascript"> | |
167 | $(function(){ |
|
167 | $(function(){ | |
168 | var defaultSourceRepo = '${c.default_repo_data['source_repo_name']}'; |
|
168 | var defaultSourceRepo = '${c.default_repo_data['source_repo_name']}'; | |
169 | var defaultSourceRepoData = ${c.default_repo_data['source_refs_json']|n}; |
|
169 | var defaultSourceRepoData = ${c.default_repo_data['source_refs_json']|n}; | |
170 | var defaultTargetRepo = '${c.default_repo_data['target_repo_name']}'; |
|
170 | var defaultTargetRepo = '${c.default_repo_data['target_repo_name']}'; | |
171 | var defaultTargetRepoData = ${c.default_repo_data['target_refs_json']|n}; |
|
171 | var defaultTargetRepoData = ${c.default_repo_data['target_refs_json']|n}; | |
172 |
|
172 | |||
173 | var $pullRequestForm = $('#pull_request_form'); |
|
173 | var $pullRequestForm = $('#pull_request_form'); | |
174 | var $sourceRepo = $('#source_repo', $pullRequestForm); |
|
174 | var $sourceRepo = $('#source_repo', $pullRequestForm); | |
175 | var $targetRepo = $('#target_repo', $pullRequestForm); |
|
175 | var $targetRepo = $('#target_repo', $pullRequestForm); | |
176 | var $sourceRef = $('#source_ref', $pullRequestForm); |
|
176 | var $sourceRef = $('#source_ref', $pullRequestForm); | |
177 | var $targetRef = $('#target_ref', $pullRequestForm); |
|
177 | var $targetRef = $('#target_ref', $pullRequestForm); | |
178 |
|
178 | |||
179 | var sourceRepo = function() { return $sourceRepo.eq(0).val() }; |
|
179 | var sourceRepo = function() { return $sourceRepo.eq(0).val() }; | |
180 | var sourceRef = function() { return $sourceRef.eq(0).val().split(':') }; |
|
180 | var sourceRef = function() { return $sourceRef.eq(0).val().split(':') }; | |
181 |
|
181 | |||
182 | var targetRepo = function() { return $targetRepo.eq(0).val() }; |
|
182 | var targetRepo = function() { return $targetRepo.eq(0).val() }; | |
183 | var targetRef = function() { return $targetRef.eq(0).val().split(':') }; |
|
183 | var targetRef = function() { return $targetRef.eq(0).val().split(':') }; | |
184 |
|
184 | |||
185 | var calculateContainerWidth = function() { |
|
185 | var calculateContainerWidth = function() { | |
186 | var maxWidth = 0; |
|
186 | var maxWidth = 0; | |
187 | var repoSelect2Containers = ['#source_repo', '#target_repo']; |
|
187 | var repoSelect2Containers = ['#source_repo', '#target_repo']; | |
188 | $.each(repoSelect2Containers, function(idx, value) { |
|
188 | $.each(repoSelect2Containers, function(idx, value) { | |
189 | $(value).select2('container').width('auto'); |
|
189 | $(value).select2('container').width('auto'); | |
190 | var curWidth = $(value).select2('container').width(); |
|
190 | var curWidth = $(value).select2('container').width(); | |
191 | if (maxWidth <= curWidth) { |
|
191 | if (maxWidth <= curWidth) { | |
192 | maxWidth = curWidth; |
|
192 | maxWidth = curWidth; | |
193 | } |
|
193 | } | |
194 | $.each(repoSelect2Containers, function(idx, value) { |
|
194 | $.each(repoSelect2Containers, function(idx, value) { | |
195 | $(value).select2('container').width(maxWidth + 10); |
|
195 | $(value).select2('container').width(maxWidth + 10); | |
196 | }); |
|
196 | }); | |
197 | }); |
|
197 | }); | |
198 | }; |
|
198 | }; | |
199 |
|
199 | |||
200 | var initRefSelection = function(selectedRef) { |
|
200 | var initRefSelection = function(selectedRef) { | |
201 | return function(element, callback) { |
|
201 | return function(element, callback) { | |
202 | // translate our select2 id into a text, it's a mapping to show |
|
202 | // translate our select2 id into a text, it's a mapping to show | |
203 | // simple label when selecting by internal ID. |
|
203 | // simple label when selecting by internal ID. | |
204 | var id, refData; |
|
204 | var id, refData; | |
205 | if (selectedRef === undefined || selectedRef === null) { |
|
205 | if (selectedRef === undefined || selectedRef === null) { | |
206 | id = element.val(); |
|
206 | id = element.val(); | |
207 | refData = element.val().split(':'); |
|
207 | refData = element.val().split(':'); | |
208 |
|
208 | |||
209 | if (refData.length !== 3){ |
|
209 | if (refData.length !== 3){ | |
210 | refData = ["", "", ""] |
|
210 | refData = ["", "", ""] | |
211 | } |
|
211 | } | |
212 | } else { |
|
212 | } else { | |
213 | id = selectedRef; |
|
213 | id = selectedRef; | |
214 | refData = selectedRef.split(':'); |
|
214 | refData = selectedRef.split(':'); | |
215 | } |
|
215 | } | |
216 |
|
216 | |||
217 | var text = refData[1]; |
|
217 | var text = refData[1]; | |
218 | if (refData[0] === 'rev') { |
|
218 | if (refData[0] === 'rev') { | |
219 | text = text.substring(0, 12); |
|
219 | text = text.substring(0, 12); | |
220 | } |
|
220 | } | |
221 |
|
221 | |||
222 | var data = {id: id, text: text}; |
|
222 | var data = {id: id, text: text}; | |
223 | callback(data); |
|
223 | callback(data); | |
224 | }; |
|
224 | }; | |
225 | }; |
|
225 | }; | |
226 |
|
226 | |||
227 | var formatRefSelection = function(item) { |
|
227 | var formatRefSelection = function(item) { | |
228 | var prefix = ''; |
|
228 | var prefix = ''; | |
229 | var refData = item.id.split(':'); |
|
229 | var refData = item.id.split(':'); | |
230 | if (refData[0] === 'branch') { |
|
230 | if (refData[0] === 'branch') { | |
231 | prefix = '<i class="icon-branch"></i>'; |
|
231 | prefix = '<i class="icon-branch"></i>'; | |
232 | } |
|
232 | } | |
233 | else if (refData[0] === 'book') { |
|
233 | else if (refData[0] === 'book') { | |
234 | prefix = '<i class="icon-bookmark"></i>'; |
|
234 | prefix = '<i class="icon-bookmark"></i>'; | |
235 | } |
|
235 | } | |
236 | else if (refData[0] === 'tag') { |
|
236 | else if (refData[0] === 'tag') { | |
237 | prefix = '<i class="icon-tag"></i>'; |
|
237 | prefix = '<i class="icon-tag"></i>'; | |
238 | } |
|
238 | } | |
239 |
|
239 | |||
240 | var originalOption = item.element; |
|
240 | var originalOption = item.element; | |
241 | return prefix + item.text; |
|
241 | return prefix + item.text; | |
242 | }; |
|
242 | }; | |
243 |
|
243 | |||
244 | // custom code mirror |
|
244 | // custom code mirror | |
245 | var codeMirrorInstance = initPullRequestsCodeMirror('#pullrequest_desc'); |
|
245 | var codeMirrorInstance = initPullRequestsCodeMirror('#pullrequest_desc'); | |
246 |
|
246 | |||
247 | reviewersController = new ReviewersController(); |
|
247 | reviewersController = new ReviewersController(); | |
248 |
|
248 | |||
249 | var queryTargetRepo = function(self, query) { |
|
249 | var queryTargetRepo = function(self, query) { | |
250 | // cache ALL results if query is empty |
|
250 | // cache ALL results if query is empty | |
251 | var cacheKey = query.term || '__'; |
|
251 | var cacheKey = query.term || '__'; | |
252 | var cachedData = self.cachedDataSource[cacheKey]; |
|
252 | var cachedData = self.cachedDataSource[cacheKey]; | |
253 |
|
253 | |||
254 | if (cachedData) { |
|
254 | if (cachedData) { | |
255 | query.callback({results: cachedData.results}); |
|
255 | query.callback({results: cachedData.results}); | |
256 | } else { |
|
256 | } else { | |
257 | $.ajax({ |
|
257 | $.ajax({ | |
258 | url: pyroutes.url('pullrequest_repo_destinations', {'repo_name': templateContext.repo_name}), |
|
258 | url: pyroutes.url('pullrequest_repo_destinations', {'repo_name': templateContext.repo_name}), | |
259 | data: {query: query.term}, |
|
259 | data: {query: query.term}, | |
260 | dataType: 'json', |
|
260 | dataType: 'json', | |
261 | type: 'GET', |
|
261 | type: 'GET', | |
262 | success: function(data) { |
|
262 | success: function(data) { | |
263 | self.cachedDataSource[cacheKey] = data; |
|
263 | self.cachedDataSource[cacheKey] = data; | |
264 | query.callback({results: data.results}); |
|
264 | query.callback({results: data.results}); | |
265 | }, |
|
265 | }, | |
266 | error: function(data, textStatus, errorThrown) { |
|
266 | error: function(data, textStatus, errorThrown) { | |
267 | alert( |
|
267 | alert( | |
268 | "Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText)); |
|
268 | "Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText)); | |
269 | } |
|
269 | } | |
270 | }); |
|
270 | }); | |
271 | } |
|
271 | } | |
272 | }; |
|
272 | }; | |
273 |
|
273 | |||
274 | var queryTargetRefs = function(initialData, query) { |
|
274 | var queryTargetRefs = function(initialData, query) { | |
275 | var data = {results: []}; |
|
275 | var data = {results: []}; | |
276 | // filter initialData |
|
276 | // filter initialData | |
277 | $.each(initialData, function() { |
|
277 | $.each(initialData, function() { | |
278 | var section = this.text; |
|
278 | var section = this.text; | |
279 | var children = []; |
|
279 | var children = []; | |
280 | $.each(this.children, function() { |
|
280 | $.each(this.children, function() { | |
281 | if (query.term.length === 0 || |
|
281 | if (query.term.length === 0 || | |
282 | this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0 ) { |
|
282 | this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0 ) { | |
283 | children.push({'id': this.id, 'text': this.text}) |
|
283 | children.push({'id': this.id, 'text': this.text}) | |
284 | } |
|
284 | } | |
285 | }); |
|
285 | }); | |
286 | data.results.push({'text': section, 'children': children}) |
|
286 | data.results.push({'text': section, 'children': children}) | |
287 | }); |
|
287 | }); | |
288 | query.callback({results: data.results}); |
|
288 | query.callback({results: data.results}); | |
289 | }; |
|
289 | }; | |
290 |
|
290 | |||
291 | var loadRepoRefDiffPreview = function() { |
|
291 | var loadRepoRefDiffPreview = function() { | |
292 |
|
292 | |||
293 | var url_data = { |
|
293 | var url_data = { | |
294 | 'repo_name': targetRepo(), |
|
294 | 'repo_name': targetRepo(), | |
295 | 'target_repo': sourceRepo(), |
|
295 | 'target_repo': sourceRepo(), | |
296 | 'source_ref': targetRef()[2], |
|
296 | 'source_ref': targetRef()[2], | |
297 | 'source_ref_type': 'rev', |
|
297 | 'source_ref_type': 'rev', | |
298 | 'target_ref': sourceRef()[2], |
|
298 | 'target_ref': sourceRef()[2], | |
299 | 'target_ref_type': 'rev', |
|
299 | 'target_ref_type': 'rev', | |
300 | 'merge': true, |
|
300 | 'merge': true, | |
301 | '_': Date.now() // bypass browser caching |
|
301 | '_': Date.now() // bypass browser caching | |
302 | }; // gather the source/target ref and repo here |
|
302 | }; // gather the source/target ref and repo here | |
303 |
|
303 | |||
304 | if (sourceRef().length !== 3 || targetRef().length !== 3) { |
|
304 | if (sourceRef().length !== 3 || targetRef().length !== 3) { | |
305 | prButtonLock(true, "${_('Please select source and target')}"); |
|
305 | prButtonLock(true, "${_('Please select source and target')}"); | |
306 | return; |
|
306 | return; | |
307 | } |
|
307 | } | |
308 | var url = pyroutes.url('repo_compare', url_data); |
|
308 | var url = pyroutes.url('repo_compare', url_data); | |
309 |
|
309 | |||
310 | // lock PR button, so we cannot send PR before it's calculated |
|
310 | // lock PR button, so we cannot send PR before it's calculated | |
311 | prButtonLock(true, "${_('Loading compare ...')}", 'compare'); |
|
311 | prButtonLock(true, "${_('Loading compare ...')}", 'compare'); | |
312 |
|
312 | |||
313 | if (loadRepoRefDiffPreview._currentRequest) { |
|
313 | if (loadRepoRefDiffPreview._currentRequest) { | |
314 | loadRepoRefDiffPreview._currentRequest.abort(); |
|
314 | loadRepoRefDiffPreview._currentRequest.abort(); | |
315 | } |
|
315 | } | |
316 |
|
316 | |||
317 | loadRepoRefDiffPreview._currentRequest = $.get(url) |
|
317 | loadRepoRefDiffPreview._currentRequest = $.get(url) | |
318 | .error(function(data, textStatus, errorThrown) { |
|
318 | .error(function(data, textStatus, errorThrown) { | |
319 | if (textStatus !== 'abort') { |
|
319 | if (textStatus !== 'abort') { | |
320 | alert( |
|
320 | alert( | |
321 | "Error while processing request.\nError code {0} ({1}).".format( |
|
321 | "Error while processing request.\nError code {0} ({1}).".format( | |
322 | data.status, data.statusText)); |
|
322 | data.status, data.statusText)); | |
323 | } |
|
323 | } | |
324 |
|
324 | |||
325 | }) |
|
325 | }) | |
326 | .done(function(data) { |
|
326 | .done(function(data) { | |
327 | loadRepoRefDiffPreview._currentRequest = null; |
|
327 | loadRepoRefDiffPreview._currentRequest = null; | |
328 | $('#pull_request_overview').html(data); |
|
328 | $('#pull_request_overview').html(data); | |
329 |
|
329 | |||
330 | var commitElements = $(data).find('tr[commit_id]'); |
|
330 | var commitElements = $(data).find('tr[commit_id]'); | |
331 |
|
331 | |||
332 | var prTitleAndDesc = getTitleAndDescription( |
|
332 | var prTitleAndDesc = getTitleAndDescription( | |
333 | sourceRef()[1], commitElements, 5); |
|
333 | sourceRef()[1], commitElements, 5); | |
334 |
|
334 | |||
335 | var title = prTitleAndDesc[0]; |
|
335 | var title = prTitleAndDesc[0]; | |
336 | var proposedDescription = prTitleAndDesc[1]; |
|
336 | var proposedDescription = prTitleAndDesc[1]; | |
337 |
|
337 | |||
338 | var useGeneratedTitle = ( |
|
338 | var useGeneratedTitle = ( | |
339 | $('#pullrequest_title').hasClass('autogenerated-title') || |
|
339 | $('#pullrequest_title').hasClass('autogenerated-title') || | |
340 | $('#pullrequest_title').val() === ""); |
|
340 | $('#pullrequest_title').val() === ""); | |
341 |
|
341 | |||
342 | if (title && useGeneratedTitle) { |
|
342 | if (title && useGeneratedTitle) { | |
343 | // use generated title if we haven't specified our own |
|
343 | // use generated title if we haven't specified our own | |
344 | $('#pullrequest_title').val(title); |
|
344 | $('#pullrequest_title').val(title); | |
345 | $('#pullrequest_title').addClass('autogenerated-title'); |
|
345 | $('#pullrequest_title').addClass('autogenerated-title'); | |
346 |
|
346 | |||
347 | } |
|
347 | } | |
348 |
|
348 | |||
349 | var useGeneratedDescription = ( |
|
349 | var useGeneratedDescription = ( | |
350 | !codeMirrorInstance._userDefinedDesc || |
|
350 | !codeMirrorInstance._userDefinedDesc || | |
351 | codeMirrorInstance.getValue() === ""); |
|
351 | codeMirrorInstance.getValue() === ""); | |
352 |
|
352 | |||
353 | if (proposedDescription && useGeneratedDescription) { |
|
353 | if (proposedDescription && useGeneratedDescription) { | |
354 | // set proposed content, if we haven't defined our own, |
|
354 | // set proposed content, if we haven't defined our own, | |
355 | // or we don't have description written |
|
355 | // or we don't have description written | |
356 | codeMirrorInstance._userDefinedDesc = false; // reset state |
|
356 | codeMirrorInstance._userDefinedDesc = false; // reset state | |
357 | codeMirrorInstance.setValue(proposedDescription); |
|
357 | codeMirrorInstance.setValue(proposedDescription); | |
358 | } |
|
358 | } | |
359 |
|
359 | |||
360 | var msg = ''; |
|
360 | var msg = ''; | |
361 | if (commitElements.length === 1) { |
|
361 | if (commitElements.length === 1) { | |
362 | msg = "${_ungettext('This pull request will consist of __COMMITS__ commit.', 'This pull request will consist of __COMMITS__ commits.', 1)}"; |
|
362 | msg = "${_ungettext('This pull request will consist of __COMMITS__ commit.', 'This pull request will consist of __COMMITS__ commits.', 1)}"; | |
363 | } else { |
|
363 | } else { | |
364 | msg = "${_ungettext('This pull request will consist of __COMMITS__ commit.', 'This pull request will consist of __COMMITS__ commits.', 2)}"; |
|
364 | msg = "${_ungettext('This pull request will consist of __COMMITS__ commit.', 'This pull request will consist of __COMMITS__ commits.', 2)}"; | |
365 | } |
|
365 | } | |
366 |
|
366 | |||
367 | msg += ' <a id="pull_request_overview_url" href="{0}" target="_blank">${_('Show detailed compare.')}</a>'.format(url); |
|
367 | msg += ' <a id="pull_request_overview_url" href="{0}" target="_blank">${_('Show detailed compare.')}</a>'.format(url); | |
368 |
|
368 | |||
369 | if (commitElements.length) { |
|
369 | if (commitElements.length) { | |
370 | var commitsLink = '<a href="#pull_request_overview"><strong>{0}</strong></a>'.format(commitElements.length); |
|
370 | var commitsLink = '<a href="#pull_request_overview"><strong>{0}</strong></a>'.format(commitElements.length); | |
371 | prButtonLock(false, msg.replace('__COMMITS__', commitsLink), 'compare'); |
|
371 | prButtonLock(false, msg.replace('__COMMITS__', commitsLink), 'compare'); | |
372 | } |
|
372 | } | |
373 | else { |
|
373 | else { | |
374 | prButtonLock(true, "${_('There are no commits to merge.')}", 'compare'); |
|
374 | prButtonLock(true, "${_('There are no commits to merge.')}", 'compare'); | |
375 | } |
|
375 | } | |
376 |
|
376 | |||
377 |
|
377 | |||
378 | }); |
|
378 | }); | |
379 | }; |
|
379 | }; | |
380 |
|
380 | |||
381 | var Select2Box = function(element, overrides) { |
|
381 | var Select2Box = function(element, overrides) { | |
382 | var globalDefaults = { |
|
382 | var globalDefaults = { | |
383 | dropdownAutoWidth: true, |
|
383 | dropdownAutoWidth: true, | |
384 | containerCssClass: "drop-menu", |
|
384 | containerCssClass: "drop-menu", | |
385 | dropdownCssClass: "drop-menu-dropdown" |
|
385 | dropdownCssClass: "drop-menu-dropdown" | |
386 | }; |
|
386 | }; | |
387 |
|
387 | |||
388 | var initSelect2 = function(defaultOptions) { |
|
388 | var initSelect2 = function(defaultOptions) { | |
389 | var options = jQuery.extend(globalDefaults, defaultOptions, overrides); |
|
389 | var options = jQuery.extend(globalDefaults, defaultOptions, overrides); | |
390 | element.select2(options); |
|
390 | element.select2(options); | |
391 | }; |
|
391 | }; | |
392 |
|
392 | |||
393 | return { |
|
393 | return { | |
394 | initRef: function() { |
|
394 | initRef: function() { | |
395 | var defaultOptions = { |
|
395 | var defaultOptions = { | |
396 | minimumResultsForSearch: 5, |
|
396 | minimumResultsForSearch: 5, | |
397 | formatSelection: formatRefSelection |
|
397 | formatSelection: formatRefSelection | |
398 | }; |
|
398 | }; | |
399 |
|
399 | |||
400 | initSelect2(defaultOptions); |
|
400 | initSelect2(defaultOptions); | |
401 | }, |
|
401 | }, | |
402 |
|
402 | |||
403 | initRepo: function(defaultValue, readOnly) { |
|
403 | initRepo: function(defaultValue, readOnly) { | |
404 | var defaultOptions = { |
|
404 | var defaultOptions = { | |
405 | initSelection : function (element, callback) { |
|
405 | initSelection : function (element, callback) { | |
406 | var data = {id: defaultValue, text: defaultValue}; |
|
406 | var data = {id: defaultValue, text: defaultValue}; | |
407 | callback(data); |
|
407 | callback(data); | |
408 | } |
|
408 | } | |
409 | }; |
|
409 | }; | |
410 |
|
410 | |||
411 | initSelect2(defaultOptions); |
|
411 | initSelect2(defaultOptions); | |
412 |
|
412 | |||
413 | element.select2('val', defaultSourceRepo); |
|
413 | element.select2('val', defaultSourceRepo); | |
414 | if (readOnly === true) { |
|
414 | if (readOnly === true) { | |
415 | element.select2('readonly', true); |
|
415 | element.select2('readonly', true); | |
416 | } |
|
416 | } | |
417 | } |
|
417 | } | |
418 | }; |
|
418 | }; | |
419 | }; |
|
419 | }; | |
420 |
|
420 | |||
421 | var initTargetRefs = function(refsData, selectedRef) { |
|
421 | var initTargetRefs = function(refsData, selectedRef) { | |
422 |
|
422 | |||
423 | Select2Box($targetRef, { |
|
423 | Select2Box($targetRef, { | |
424 | placeholder: "${_('Select commit reference')}", |
|
424 | placeholder: "${_('Select commit reference')}", | |
425 | query: function(query) { |
|
425 | query: function(query) { | |
426 | queryTargetRefs(refsData, query); |
|
426 | queryTargetRefs(refsData, query); | |
427 | }, |
|
427 | }, | |
428 | initSelection : initRefSelection(selectedRef) |
|
428 | initSelection : initRefSelection(selectedRef) | |
429 | }).initRef(); |
|
429 | }).initRef(); | |
430 |
|
430 | |||
431 | if (!(selectedRef === undefined)) { |
|
431 | if (!(selectedRef === undefined)) { | |
432 | $targetRef.select2('val', selectedRef); |
|
432 | $targetRef.select2('val', selectedRef); | |
433 | } |
|
433 | } | |
434 | }; |
|
434 | }; | |
435 |
|
435 | |||
436 | var targetRepoChanged = function(repoData) { |
|
436 | var targetRepoChanged = function(repoData) { | |
437 | // generate new DESC of target repo displayed next to select |
|
437 | // generate new DESC of target repo displayed next to select | |
438 | var prLink = pyroutes.url('pullrequest_new', {'repo_name': repoData['name']}); |
|
438 | var prLink = pyroutes.url('pullrequest_new', {'repo_name': repoData['name']}); | |
439 | $('#target_repo_desc').html( |
|
439 | $('#target_repo_desc').html( | |
440 | "<strong>${_('Target repository')}</strong>: {0}. <a href=\"{1}\">Switch base, and use as source.</a>".format(repoData['description'], prLink) |
|
440 | "<strong>${_('Target repository')}</strong>: {0}. <a href=\"{1}\">Switch base, and use as source.</a>".format(repoData['description'], prLink) | |
441 | ); |
|
441 | ); | |
442 |
|
442 | |||
443 | // generate dynamic select2 for refs. |
|
443 | // generate dynamic select2 for refs. | |
444 | initTargetRefs(repoData['refs']['select2_refs'], |
|
444 | initTargetRefs(repoData['refs']['select2_refs'], | |
445 | repoData['refs']['selected_ref']); |
|
445 | repoData['refs']['selected_ref']); | |
446 |
|
446 | |||
447 | }; |
|
447 | }; | |
448 |
|
448 | |||
449 | var sourceRefSelect2 = Select2Box($sourceRef, { |
|
449 | var sourceRefSelect2 = Select2Box($sourceRef, { | |
450 | placeholder: "${_('Select commit reference')}", |
|
450 | placeholder: "${_('Select commit reference')}", | |
451 | query: function(query) { |
|
451 | query: function(query) { | |
452 | var initialData = defaultSourceRepoData['refs']['select2_refs']; |
|
452 | var initialData = defaultSourceRepoData['refs']['select2_refs']; | |
453 | queryTargetRefs(initialData, query) |
|
453 | queryTargetRefs(initialData, query) | |
454 | }, |
|
454 | }, | |
455 | initSelection: initRefSelection() |
|
455 | initSelection: initRefSelection() | |
456 | } |
|
456 | } | |
457 | ); |
|
457 | ); | |
458 |
|
458 | |||
459 | var sourceRepoSelect2 = Select2Box($sourceRepo, { |
|
459 | var sourceRepoSelect2 = Select2Box($sourceRepo, { | |
460 | query: function(query) {} |
|
460 | query: function(query) {} | |
461 | }); |
|
461 | }); | |
462 |
|
462 | |||
463 | var targetRepoSelect2 = Select2Box($targetRepo, { |
|
463 | var targetRepoSelect2 = Select2Box($targetRepo, { | |
464 | cachedDataSource: {}, |
|
464 | cachedDataSource: {}, | |
465 | query: $.debounce(250, function(query) { |
|
465 | query: $.debounce(250, function(query) { | |
466 | queryTargetRepo(this, query); |
|
466 | queryTargetRepo(this, query); | |
467 | }), |
|
467 | }), | |
468 | formatResult: formatResult |
|
468 | formatResult: formatRepoResult | |
469 | }); |
|
469 | }); | |
470 |
|
470 | |||
471 | sourceRefSelect2.initRef(); |
|
471 | sourceRefSelect2.initRef(); | |
472 |
|
472 | |||
473 | sourceRepoSelect2.initRepo(defaultSourceRepo, true); |
|
473 | sourceRepoSelect2.initRepo(defaultSourceRepo, true); | |
474 |
|
474 | |||
475 | targetRepoSelect2.initRepo(defaultTargetRepo, false); |
|
475 | targetRepoSelect2.initRepo(defaultTargetRepo, false); | |
476 |
|
476 | |||
477 | $sourceRef.on('change', function(e){ |
|
477 | $sourceRef.on('change', function(e){ | |
478 | loadRepoRefDiffPreview(); |
|
478 | loadRepoRefDiffPreview(); | |
479 | reviewersController.loadDefaultReviewers( |
|
479 | reviewersController.loadDefaultReviewers( | |
480 | sourceRepo(), sourceRef(), targetRepo(), targetRef()); |
|
480 | sourceRepo(), sourceRef(), targetRepo(), targetRef()); | |
481 | }); |
|
481 | }); | |
482 |
|
482 | |||
483 | $targetRef.on('change', function(e){ |
|
483 | $targetRef.on('change', function(e){ | |
484 | loadRepoRefDiffPreview(); |
|
484 | loadRepoRefDiffPreview(); | |
485 | reviewersController.loadDefaultReviewers( |
|
485 | reviewersController.loadDefaultReviewers( | |
486 | sourceRepo(), sourceRef(), targetRepo(), targetRef()); |
|
486 | sourceRepo(), sourceRef(), targetRepo(), targetRef()); | |
487 | }); |
|
487 | }); | |
488 |
|
488 | |||
489 | $targetRepo.on('change', function(e){ |
|
489 | $targetRepo.on('change', function(e){ | |
490 | var repoName = $(this).val(); |
|
490 | var repoName = $(this).val(); | |
491 | calculateContainerWidth(); |
|
491 | calculateContainerWidth(); | |
492 | $targetRef.select2('destroy'); |
|
492 | $targetRef.select2('destroy'); | |
493 | $('#target_ref_loading').show(); |
|
493 | $('#target_ref_loading').show(); | |
494 |
|
494 | |||
495 | $.ajax({ |
|
495 | $.ajax({ | |
496 | url: pyroutes.url('pullrequest_repo_refs', |
|
496 | url: pyroutes.url('pullrequest_repo_refs', | |
497 | {'repo_name': templateContext.repo_name, 'target_repo_name':repoName}), |
|
497 | {'repo_name': templateContext.repo_name, 'target_repo_name':repoName}), | |
498 | data: {}, |
|
498 | data: {}, | |
499 | dataType: 'json', |
|
499 | dataType: 'json', | |
500 | type: 'GET', |
|
500 | type: 'GET', | |
501 | success: function(data) { |
|
501 | success: function(data) { | |
502 | $('#target_ref_loading').hide(); |
|
502 | $('#target_ref_loading').hide(); | |
503 | targetRepoChanged(data); |
|
503 | targetRepoChanged(data); | |
504 | loadRepoRefDiffPreview(); |
|
504 | loadRepoRefDiffPreview(); | |
505 | }, |
|
505 | }, | |
506 | error: function(data, textStatus, errorThrown) { |
|
506 | error: function(data, textStatus, errorThrown) { | |
507 | alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText)); |
|
507 | alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText)); | |
508 | } |
|
508 | } | |
509 | }) |
|
509 | }) | |
510 |
|
510 | |||
511 | }); |
|
511 | }); | |
512 |
|
512 | |||
513 | prButtonLock(true, "${_('Please select source and target')}", 'all'); |
|
513 | prButtonLock(true, "${_('Please select source and target')}", 'all'); | |
514 |
|
514 | |||
515 | // auto-load on init, the target refs select2 |
|
515 | // auto-load on init, the target refs select2 | |
516 | calculateContainerWidth(); |
|
516 | calculateContainerWidth(); | |
517 | targetRepoChanged(defaultTargetRepoData); |
|
517 | targetRepoChanged(defaultTargetRepoData); | |
518 |
|
518 | |||
519 | $('#pullrequest_title').on('keyup', function(e){ |
|
519 | $('#pullrequest_title').on('keyup', function(e){ | |
520 | $(this).removeClass('autogenerated-title'); |
|
520 | $(this).removeClass('autogenerated-title'); | |
521 | }); |
|
521 | }); | |
522 |
|
522 | |||
523 | % if c.default_source_ref: |
|
523 | % if c.default_source_ref: | |
524 | // in case we have a pre-selected value, use it now |
|
524 | // in case we have a pre-selected value, use it now | |
525 | $sourceRef.select2('val', '${c.default_source_ref}'); |
|
525 | $sourceRef.select2('val', '${c.default_source_ref}'); | |
526 | // diff preview load |
|
526 | // diff preview load | |
527 | loadRepoRefDiffPreview(); |
|
527 | loadRepoRefDiffPreview(); | |
528 | // default reviewers |
|
528 | // default reviewers | |
529 | reviewersController.loadDefaultReviewers( |
|
529 | reviewersController.loadDefaultReviewers( | |
530 | sourceRepo(), sourceRef(), targetRepo(), targetRef()); |
|
530 | sourceRepo(), sourceRef(), targetRepo(), targetRef()); | |
531 | % endif |
|
531 | % endif | |
532 |
|
532 | |||
533 | ReviewerAutoComplete('#user'); |
|
533 | ReviewerAutoComplete('#user'); | |
534 | }); |
|
534 | }); | |
535 | </script> |
|
535 | </script> | |
536 |
|
536 | |||
537 | </%def> |
|
537 | </%def> |
General Comments 0
You need to be logged in to leave comments.
Login now