Show More
@@ -1,360 +1,374 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 |
|
2 | |||
3 | # Copyright (C) 2016-2020 RhodeCode GmbH |
|
3 | # Copyright (C) 2016-2020 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 | import datetime |
|
20 | import datetime | |
21 | import logging |
|
21 | import logging | |
22 | import time |
|
22 | import time | |
23 |
|
23 | |||
24 | import formencode |
|
24 | import formencode | |
25 | import formencode.htmlfill |
|
25 | import formencode.htmlfill | |
26 |
|
26 | |||
27 | from pyramid.httpexceptions import HTTPFound, HTTPForbidden |
|
27 | from pyramid.httpexceptions import HTTPFound, HTTPForbidden | |
28 | from pyramid.view import view_config |
|
28 | from pyramid.view import view_config | |
29 | from pyramid.renderers import render |
|
29 | from pyramid.renderers import render | |
30 | from pyramid.response import Response |
|
30 | from pyramid.response import Response | |
31 |
|
31 | |||
32 | from rhodecode import events |
|
32 | from rhodecode import events | |
33 | from rhodecode.apps._base import BaseAppView, DataGridAppView |
|
33 | from rhodecode.apps._base import BaseAppView, DataGridAppView | |
34 |
|
34 | |||
35 | from rhodecode.lib.auth import ( |
|
35 | from rhodecode.lib.auth import ( | |
36 | LoginRequired, CSRFRequired, NotAnonymous, |
|
36 | LoginRequired, CSRFRequired, NotAnonymous, | |
37 | HasPermissionAny, HasRepoGroupPermissionAny) |
|
37 | HasPermissionAny, HasRepoGroupPermissionAny) | |
38 | from rhodecode.lib import helpers as h, audit_logger |
|
38 | from rhodecode.lib import helpers as h, audit_logger | |
39 | from rhodecode.lib.utils2 import safe_int, safe_unicode, datetime_to_time |
|
39 | from rhodecode.lib.utils2 import safe_int, safe_unicode, datetime_to_time | |
40 | from rhodecode.model.forms import RepoGroupForm |
|
40 | from rhodecode.model.forms import RepoGroupForm | |
41 | from rhodecode.model.permission import PermissionModel |
|
41 | from rhodecode.model.permission import PermissionModel | |
42 | from rhodecode.model.repo_group import RepoGroupModel |
|
42 | from rhodecode.model.repo_group import RepoGroupModel | |
43 | from rhodecode.model.scm import RepoGroupList |
|
43 | from rhodecode.model.scm import RepoGroupList | |
44 | from rhodecode.model.db import ( |
|
44 | from rhodecode.model.db import ( | |
45 | or_, count, func, in_filter_generator, Session, RepoGroup, User, Repository) |
|
45 | or_, count, func, in_filter_generator, Session, RepoGroup, User, Repository) | |
46 |
|
46 | |||
47 | log = logging.getLogger(__name__) |
|
47 | log = logging.getLogger(__name__) | |
48 |
|
48 | |||
49 |
|
49 | |||
50 | class AdminRepoGroupsView(BaseAppView, DataGridAppView): |
|
50 | class AdminRepoGroupsView(BaseAppView, DataGridAppView): | |
51 |
|
51 | |||
52 | def load_default_context(self): |
|
52 | def load_default_context(self): | |
53 | c = self._get_local_tmpl_context() |
|
53 | c = self._get_local_tmpl_context() | |
54 |
|
54 | |||
55 | return c |
|
55 | return c | |
56 |
|
56 | |||
57 | def _load_form_data(self, c): |
|
57 | def _load_form_data(self, c): | |
58 | allow_empty_group = False |
|
58 | allow_empty_group = False | |
59 |
|
59 | |||
60 | if self._can_create_repo_group(): |
|
60 | if self._can_create_repo_group(): | |
61 | # we're global admin, we're ok and we can create TOP level groups |
|
61 | # we're global admin, we're ok and we can create TOP level groups | |
62 | allow_empty_group = True |
|
62 | allow_empty_group = True | |
63 |
|
63 | |||
64 | # override the choices for this form, we need to filter choices |
|
64 | # override the choices for this form, we need to filter choices | |
65 | # and display only those we have ADMIN right |
|
65 | # and display only those we have ADMIN right | |
66 | groups_with_admin_rights = RepoGroupList( |
|
66 | groups_with_admin_rights = RepoGroupList( | |
67 | RepoGroup.query().all(), |
|
67 | RepoGroup.query().all(), | |
68 | perm_set=['group.admin'], extra_kwargs=dict(user=self._rhodecode_user)) |
|
68 | perm_set=['group.admin'], extra_kwargs=dict(user=self._rhodecode_user)) | |
69 | c.repo_groups = RepoGroup.groups_choices( |
|
69 | c.repo_groups = RepoGroup.groups_choices( | |
70 | groups=groups_with_admin_rights, |
|
70 | groups=groups_with_admin_rights, | |
71 | show_empty_group=allow_empty_group) |
|
71 | show_empty_group=allow_empty_group) | |
|
72 | c.personal_repo_group = self._rhodecode_user.personal_repo_group | |||
72 |
|
73 | |||
73 | def _can_create_repo_group(self, parent_group_id=None): |
|
74 | def _can_create_repo_group(self, parent_group_id=None): | |
74 | is_admin = HasPermissionAny('hg.admin')('group create controller') |
|
75 | is_admin = HasPermissionAny('hg.admin')('group create controller') | |
75 | create_repo_group = HasPermissionAny( |
|
76 | create_repo_group = HasPermissionAny( | |
76 | 'hg.repogroup.create.true')('group create controller') |
|
77 | 'hg.repogroup.create.true')('group create controller') | |
77 | if is_admin or (create_repo_group and not parent_group_id): |
|
78 | if is_admin or (create_repo_group and not parent_group_id): | |
78 | # we're global admin, or we have global repo group create |
|
79 | # we're global admin, or we have global repo group create | |
79 | # permission |
|
80 | # permission | |
80 | # we're ok and we can create TOP level groups |
|
81 | # we're ok and we can create TOP level groups | |
81 | return True |
|
82 | return True | |
82 | elif parent_group_id: |
|
83 | elif parent_group_id: | |
83 | # we check the permission if we can write to parent group |
|
84 | # we check the permission if we can write to parent group | |
84 | group = RepoGroup.get(parent_group_id) |
|
85 | group = RepoGroup.get(parent_group_id) | |
85 | group_name = group.group_name if group else None |
|
86 | group_name = group.group_name if group else None | |
86 | if HasRepoGroupPermissionAny('group.admin')( |
|
87 | if HasRepoGroupPermissionAny('group.admin')( | |
87 | group_name, 'check if user is an admin of group'): |
|
88 | group_name, 'check if user is an admin of group'): | |
88 | # we're an admin of passed in group, we're ok. |
|
89 | # we're an admin of passed in group, we're ok. | |
89 | return True |
|
90 | return True | |
90 | else: |
|
91 | else: | |
91 | return False |
|
92 | return False | |
92 | return False |
|
93 | return False | |
93 |
|
94 | |||
94 | # permission check in data loading of |
|
95 | # permission check in data loading of | |
95 | # `repo_group_list_data` via RepoGroupList |
|
96 | # `repo_group_list_data` via RepoGroupList | |
96 | @LoginRequired() |
|
97 | @LoginRequired() | |
97 | @NotAnonymous() |
|
98 | @NotAnonymous() | |
98 | @view_config( |
|
99 | @view_config( | |
99 | route_name='repo_groups', request_method='GET', |
|
100 | route_name='repo_groups', request_method='GET', | |
100 | renderer='rhodecode:templates/admin/repo_groups/repo_groups.mako') |
|
101 | renderer='rhodecode:templates/admin/repo_groups/repo_groups.mako') | |
101 | def repo_group_list(self): |
|
102 | def repo_group_list(self): | |
102 | c = self.load_default_context() |
|
103 | c = self.load_default_context() | |
103 | return self._get_template_context(c) |
|
104 | return self._get_template_context(c) | |
104 |
|
105 | |||
105 | # permission check inside |
|
106 | # permission check inside | |
106 | @LoginRequired() |
|
107 | @LoginRequired() | |
107 | @NotAnonymous() |
|
108 | @NotAnonymous() | |
108 | @view_config( |
|
109 | @view_config( | |
109 | route_name='repo_groups_data', request_method='GET', |
|
110 | route_name='repo_groups_data', request_method='GET', | |
110 | renderer='json_ext', xhr=True) |
|
111 | renderer='json_ext', xhr=True) | |
111 | def repo_group_list_data(self): |
|
112 | def repo_group_list_data(self): | |
112 | self.load_default_context() |
|
113 | self.load_default_context() | |
113 | column_map = { |
|
114 | column_map = { | |
114 | 'name': 'group_name_hash', |
|
115 | 'name': 'group_name_hash', | |
115 | 'desc': 'group_description', |
|
116 | 'desc': 'group_description', | |
116 | 'last_change': 'updated_on', |
|
117 | 'last_change': 'updated_on', | |
117 | 'top_level_repos': 'repos_total', |
|
118 | 'top_level_repos': 'repos_total', | |
118 | 'owner': 'user_username', |
|
119 | 'owner': 'user_username', | |
119 | } |
|
120 | } | |
120 | draw, start, limit = self._extract_chunk(self.request) |
|
121 | draw, start, limit = self._extract_chunk(self.request) | |
121 | search_q, order_by, order_dir = self._extract_ordering( |
|
122 | search_q, order_by, order_dir = self._extract_ordering( | |
122 | self.request, column_map=column_map) |
|
123 | self.request, column_map=column_map) | |
123 |
|
124 | |||
124 | _render = self.request.get_partial_renderer( |
|
125 | _render = self.request.get_partial_renderer( | |
125 | 'rhodecode:templates/data_table/_dt_elements.mako') |
|
126 | 'rhodecode:templates/data_table/_dt_elements.mako') | |
126 | c = _render.get_call_context() |
|
127 | c = _render.get_call_context() | |
127 |
|
128 | |||
128 | def quick_menu(repo_group_name): |
|
129 | def quick_menu(repo_group_name): | |
129 | return _render('quick_repo_group_menu', repo_group_name) |
|
130 | return _render('quick_repo_group_menu', repo_group_name) | |
130 |
|
131 | |||
131 | def repo_group_lnk(repo_group_name): |
|
132 | def repo_group_lnk(repo_group_name): | |
132 | return _render('repo_group_name', repo_group_name) |
|
133 | return _render('repo_group_name', repo_group_name) | |
133 |
|
134 | |||
134 | def last_change(last_change): |
|
135 | def last_change(last_change): | |
135 | if isinstance(last_change, datetime.datetime) and not last_change.tzinfo: |
|
136 | if isinstance(last_change, datetime.datetime) and not last_change.tzinfo: | |
136 | ts = time.time() |
|
137 | ts = time.time() | |
137 | utc_offset = (datetime.datetime.fromtimestamp(ts) |
|
138 | utc_offset = (datetime.datetime.fromtimestamp(ts) | |
138 | - datetime.datetime.utcfromtimestamp(ts)).total_seconds() |
|
139 | - datetime.datetime.utcfromtimestamp(ts)).total_seconds() | |
139 | last_change = last_change + datetime.timedelta(seconds=utc_offset) |
|
140 | last_change = last_change + datetime.timedelta(seconds=utc_offset) | |
140 | return _render("last_change", last_change) |
|
141 | return _render("last_change", last_change) | |
141 |
|
142 | |||
142 | def desc(desc, personal): |
|
143 | def desc(desc, personal): | |
143 | return _render( |
|
144 | return _render( | |
144 | 'repo_group_desc', desc, personal, c.visual.stylify_metatags) |
|
145 | 'repo_group_desc', desc, personal, c.visual.stylify_metatags) | |
145 |
|
146 | |||
146 | def repo_group_actions(repo_group_id, repo_group_name, gr_count): |
|
147 | def repo_group_actions(repo_group_id, repo_group_name, gr_count): | |
147 | return _render( |
|
148 | return _render( | |
148 | 'repo_group_actions', repo_group_id, repo_group_name, gr_count) |
|
149 | 'repo_group_actions', repo_group_id, repo_group_name, gr_count) | |
149 |
|
150 | |||
150 | def user_profile(username): |
|
151 | def user_profile(username): | |
151 | return _render('user_profile', username) |
|
152 | return _render('user_profile', username) | |
152 |
|
153 | |||
153 | _perms = ['group.admin'] |
|
154 | _perms = ['group.admin'] | |
154 | allowed_ids = [-1] + self._rhodecode_user.repo_group_acl_ids_from_stack(_perms) |
|
155 | allowed_ids = [-1] + self._rhodecode_user.repo_group_acl_ids_from_stack(_perms) | |
155 |
|
156 | |||
156 | repo_groups_data_total_count = RepoGroup.query()\ |
|
157 | repo_groups_data_total_count = RepoGroup.query()\ | |
157 | .filter(or_( |
|
158 | .filter(or_( | |
158 | # generate multiple IN to fix limitation problems |
|
159 | # generate multiple IN to fix limitation problems | |
159 | *in_filter_generator(RepoGroup.group_id, allowed_ids) |
|
160 | *in_filter_generator(RepoGroup.group_id, allowed_ids) | |
160 | )) \ |
|
161 | )) \ | |
161 | .count() |
|
162 | .count() | |
162 |
|
163 | |||
163 | repo_groups_data_total_inactive_count = RepoGroup.query()\ |
|
164 | repo_groups_data_total_inactive_count = RepoGroup.query()\ | |
164 | .filter(RepoGroup.group_id.in_(allowed_ids))\ |
|
165 | .filter(RepoGroup.group_id.in_(allowed_ids))\ | |
165 | .count() |
|
166 | .count() | |
166 |
|
167 | |||
167 | repo_count = count(Repository.repo_id) |
|
168 | repo_count = count(Repository.repo_id) | |
168 | base_q = Session.query( |
|
169 | base_q = Session.query( | |
169 | RepoGroup.group_name, |
|
170 | RepoGroup.group_name, | |
170 | RepoGroup.group_name_hash, |
|
171 | RepoGroup.group_name_hash, | |
171 | RepoGroup.group_description, |
|
172 | RepoGroup.group_description, | |
172 | RepoGroup.group_id, |
|
173 | RepoGroup.group_id, | |
173 | RepoGroup.personal, |
|
174 | RepoGroup.personal, | |
174 | RepoGroup.updated_on, |
|
175 | RepoGroup.updated_on, | |
175 | User, |
|
176 | User, | |
176 | repo_count.label('repos_count') |
|
177 | repo_count.label('repos_count') | |
177 | ) \ |
|
178 | ) \ | |
178 | .filter(or_( |
|
179 | .filter(or_( | |
179 | # generate multiple IN to fix limitation problems |
|
180 | # generate multiple IN to fix limitation problems | |
180 | *in_filter_generator(RepoGroup.group_id, allowed_ids) |
|
181 | *in_filter_generator(RepoGroup.group_id, allowed_ids) | |
181 | )) \ |
|
182 | )) \ | |
182 | .outerjoin(Repository, Repository.group_id == RepoGroup.group_id) \ |
|
183 | .outerjoin(Repository, Repository.group_id == RepoGroup.group_id) \ | |
183 | .join(User, User.user_id == RepoGroup.user_id) \ |
|
184 | .join(User, User.user_id == RepoGroup.user_id) \ | |
184 | .group_by(RepoGroup, User) |
|
185 | .group_by(RepoGroup, User) | |
185 |
|
186 | |||
186 | if search_q: |
|
187 | if search_q: | |
187 | like_expression = u'%{}%'.format(safe_unicode(search_q)) |
|
188 | like_expression = u'%{}%'.format(safe_unicode(search_q)) | |
188 | base_q = base_q.filter(or_( |
|
189 | base_q = base_q.filter(or_( | |
189 | RepoGroup.group_name.ilike(like_expression), |
|
190 | RepoGroup.group_name.ilike(like_expression), | |
190 | )) |
|
191 | )) | |
191 |
|
192 | |||
192 | repo_groups_data_total_filtered_count = base_q.count() |
|
193 | repo_groups_data_total_filtered_count = base_q.count() | |
193 | # the inactive isn't really used, but we still make it same as other data grids |
|
194 | # the inactive isn't really used, but we still make it same as other data grids | |
194 | # which use inactive (users,user groups) |
|
195 | # which use inactive (users,user groups) | |
195 | repo_groups_data_total_filtered_inactive_count = repo_groups_data_total_filtered_count |
|
196 | repo_groups_data_total_filtered_inactive_count = repo_groups_data_total_filtered_count | |
196 |
|
197 | |||
197 | sort_defined = False |
|
198 | sort_defined = False | |
198 | if order_by == 'group_name': |
|
199 | if order_by == 'group_name': | |
199 | sort_col = func.lower(RepoGroup.group_name) |
|
200 | sort_col = func.lower(RepoGroup.group_name) | |
200 | sort_defined = True |
|
201 | sort_defined = True | |
201 | elif order_by == 'repos_total': |
|
202 | elif order_by == 'repos_total': | |
202 | sort_col = repo_count |
|
203 | sort_col = repo_count | |
203 | sort_defined = True |
|
204 | sort_defined = True | |
204 | elif order_by == 'user_username': |
|
205 | elif order_by == 'user_username': | |
205 | sort_col = User.username |
|
206 | sort_col = User.username | |
206 | else: |
|
207 | else: | |
207 | sort_col = getattr(RepoGroup, order_by, None) |
|
208 | sort_col = getattr(RepoGroup, order_by, None) | |
208 |
|
209 | |||
209 | if sort_defined or sort_col: |
|
210 | if sort_defined or sort_col: | |
210 | if order_dir == 'asc': |
|
211 | if order_dir == 'asc': | |
211 | sort_col = sort_col.asc() |
|
212 | sort_col = sort_col.asc() | |
212 | else: |
|
213 | else: | |
213 | sort_col = sort_col.desc() |
|
214 | sort_col = sort_col.desc() | |
214 |
|
215 | |||
215 | base_q = base_q.order_by(sort_col) |
|
216 | base_q = base_q.order_by(sort_col) | |
216 | base_q = base_q.offset(start).limit(limit) |
|
217 | base_q = base_q.offset(start).limit(limit) | |
217 |
|
218 | |||
218 | # authenticated access to user groups |
|
219 | # authenticated access to user groups | |
219 | auth_repo_group_list = base_q.all() |
|
220 | auth_repo_group_list = base_q.all() | |
220 |
|
221 | |||
221 | repo_groups_data = [] |
|
222 | repo_groups_data = [] | |
222 | for repo_gr in auth_repo_group_list: |
|
223 | for repo_gr in auth_repo_group_list: | |
223 | row = { |
|
224 | row = { | |
224 | "menu": quick_menu(repo_gr.group_name), |
|
225 | "menu": quick_menu(repo_gr.group_name), | |
225 | "name": repo_group_lnk(repo_gr.group_name), |
|
226 | "name": repo_group_lnk(repo_gr.group_name), | |
226 |
|
227 | |||
227 | "last_change": last_change(repo_gr.updated_on), |
|
228 | "last_change": last_change(repo_gr.updated_on), | |
228 |
|
229 | |||
229 | "last_changeset": "", |
|
230 | "last_changeset": "", | |
230 | "last_changeset_raw": "", |
|
231 | "last_changeset_raw": "", | |
231 |
|
232 | |||
232 | "desc": desc(repo_gr.group_description, repo_gr.personal), |
|
233 | "desc": desc(repo_gr.group_description, repo_gr.personal), | |
233 | "owner": user_profile(repo_gr.User.username), |
|
234 | "owner": user_profile(repo_gr.User.username), | |
234 | "top_level_repos": repo_gr.repos_count, |
|
235 | "top_level_repos": repo_gr.repos_count, | |
235 | "action": repo_group_actions( |
|
236 | "action": repo_group_actions( | |
236 | repo_gr.group_id, repo_gr.group_name, repo_gr.repos_count), |
|
237 | repo_gr.group_id, repo_gr.group_name, repo_gr.repos_count), | |
237 |
|
238 | |||
238 | } |
|
239 | } | |
239 |
|
240 | |||
240 | repo_groups_data.append(row) |
|
241 | repo_groups_data.append(row) | |
241 |
|
242 | |||
242 | data = ({ |
|
243 | data = ({ | |
243 | 'draw': draw, |
|
244 | 'draw': draw, | |
244 | 'data': repo_groups_data, |
|
245 | 'data': repo_groups_data, | |
245 | 'recordsTotal': repo_groups_data_total_count, |
|
246 | 'recordsTotal': repo_groups_data_total_count, | |
246 | 'recordsTotalInactive': repo_groups_data_total_inactive_count, |
|
247 | 'recordsTotalInactive': repo_groups_data_total_inactive_count, | |
247 | 'recordsFiltered': repo_groups_data_total_filtered_count, |
|
248 | 'recordsFiltered': repo_groups_data_total_filtered_count, | |
248 | 'recordsFilteredInactive': repo_groups_data_total_filtered_inactive_count, |
|
249 | 'recordsFilteredInactive': repo_groups_data_total_filtered_inactive_count, | |
249 | }) |
|
250 | }) | |
250 |
|
251 | |||
251 | return data |
|
252 | return data | |
252 |
|
253 | |||
253 | @LoginRequired() |
|
254 | @LoginRequired() | |
254 | @NotAnonymous() |
|
255 | @NotAnonymous() | |
255 | # perm checks inside |
|
256 | # perm checks inside | |
256 | @view_config( |
|
257 | @view_config( | |
257 | route_name='repo_group_new', request_method='GET', |
|
258 | route_name='repo_group_new', request_method='GET', | |
258 | renderer='rhodecode:templates/admin/repo_groups/repo_group_add.mako') |
|
259 | renderer='rhodecode:templates/admin/repo_groups/repo_group_add.mako') | |
259 | def repo_group_new(self): |
|
260 | def repo_group_new(self): | |
260 | c = self.load_default_context() |
|
261 | c = self.load_default_context() | |
261 |
|
262 | |||
262 | # perm check for admin, create_group perm or admin of parent_group |
|
263 | # perm check for admin, create_group perm or admin of parent_group | |
263 | parent_group_id = safe_int(self.request.GET.get('parent_group')) |
|
264 | parent_group_id = safe_int(self.request.GET.get('parent_group')) | |
|
265 | _gr = RepoGroup.get(parent_group_id) | |||
264 | if not self._can_create_repo_group(parent_group_id): |
|
266 | if not self._can_create_repo_group(parent_group_id): | |
265 | raise HTTPForbidden() |
|
267 | raise HTTPForbidden() | |
266 |
|
268 | |||
267 | self._load_form_data(c) |
|
269 | self._load_form_data(c) | |
268 |
|
270 | |||
269 | defaults = {} # Future proof for default of repo group |
|
271 | defaults = {} # Future proof for default of repo group | |
|
272 | ||||
|
273 | parent_group_choice = '-1' | |||
|
274 | if not self._rhodecode_user.is_admin and self._rhodecode_user.personal_repo_group: | |||
|
275 | parent_group_choice = self._rhodecode_user.personal_repo_group | |||
|
276 | ||||
|
277 | if parent_group_id and _gr: | |||
|
278 | if parent_group_id in [x[0] for x in c.repo_groups]: | |||
|
279 | parent_group_choice = safe_unicode(parent_group_id) | |||
|
280 | ||||
|
281 | defaults.update({'group_parent_id': parent_group_choice}) | |||
|
282 | ||||
270 | data = render( |
|
283 | data = render( | |
271 | 'rhodecode:templates/admin/repo_groups/repo_group_add.mako', |
|
284 | 'rhodecode:templates/admin/repo_groups/repo_group_add.mako', | |
272 | self._get_template_context(c), self.request) |
|
285 | self._get_template_context(c), self.request) | |
|
286 | ||||
273 | html = formencode.htmlfill.render( |
|
287 | html = formencode.htmlfill.render( | |
274 | data, |
|
288 | data, | |
275 | defaults=defaults, |
|
289 | defaults=defaults, | |
276 | encoding="UTF-8", |
|
290 | encoding="UTF-8", | |
277 | force_defaults=False |
|
291 | force_defaults=False | |
278 | ) |
|
292 | ) | |
279 | return Response(html) |
|
293 | return Response(html) | |
280 |
|
294 | |||
281 | @LoginRequired() |
|
295 | @LoginRequired() | |
282 | @NotAnonymous() |
|
296 | @NotAnonymous() | |
283 | @CSRFRequired() |
|
297 | @CSRFRequired() | |
284 | # perm checks inside |
|
298 | # perm checks inside | |
285 | @view_config( |
|
299 | @view_config( | |
286 | route_name='repo_group_create', request_method='POST', |
|
300 | route_name='repo_group_create', request_method='POST', | |
287 | renderer='rhodecode:templates/admin/repo_groups/repo_group_add.mako') |
|
301 | renderer='rhodecode:templates/admin/repo_groups/repo_group_add.mako') | |
288 | def repo_group_create(self): |
|
302 | def repo_group_create(self): | |
289 | c = self.load_default_context() |
|
303 | c = self.load_default_context() | |
290 | _ = self.request.translate |
|
304 | _ = self.request.translate | |
291 |
|
305 | |||
292 | parent_group_id = safe_int(self.request.POST.get('group_parent_id')) |
|
306 | parent_group_id = safe_int(self.request.POST.get('group_parent_id')) | |
293 | can_create = self._can_create_repo_group(parent_group_id) |
|
307 | can_create = self._can_create_repo_group(parent_group_id) | |
294 |
|
308 | |||
295 | self._load_form_data(c) |
|
309 | self._load_form_data(c) | |
296 | # permissions for can create group based on parent_id are checked |
|
310 | # permissions for can create group based on parent_id are checked | |
297 | # here in the Form |
|
311 | # here in the Form | |
298 | available_groups = map(lambda k: safe_unicode(k[0]), c.repo_groups) |
|
312 | available_groups = map(lambda k: safe_unicode(k[0]), c.repo_groups) | |
299 | repo_group_form = RepoGroupForm( |
|
313 | repo_group_form = RepoGroupForm( | |
300 | self.request.translate, available_groups=available_groups, |
|
314 | self.request.translate, available_groups=available_groups, | |
301 | can_create_in_root=can_create)() |
|
315 | can_create_in_root=can_create)() | |
302 |
|
316 | |||
303 | repo_group_name = self.request.POST.get('group_name') |
|
317 | repo_group_name = self.request.POST.get('group_name') | |
304 | try: |
|
318 | try: | |
305 | owner = self._rhodecode_user |
|
319 | owner = self._rhodecode_user | |
306 | form_result = repo_group_form.to_python(dict(self.request.POST)) |
|
320 | form_result = repo_group_form.to_python(dict(self.request.POST)) | |
307 | copy_permissions = form_result.get('group_copy_permissions') |
|
321 | copy_permissions = form_result.get('group_copy_permissions') | |
308 | repo_group = RepoGroupModel().create( |
|
322 | repo_group = RepoGroupModel().create( | |
309 | group_name=form_result['group_name_full'], |
|
323 | group_name=form_result['group_name_full'], | |
310 | group_description=form_result['group_description'], |
|
324 | group_description=form_result['group_description'], | |
311 | owner=owner.user_id, |
|
325 | owner=owner.user_id, | |
312 | copy_permissions=form_result['group_copy_permissions'] |
|
326 | copy_permissions=form_result['group_copy_permissions'] | |
313 | ) |
|
327 | ) | |
314 | Session().flush() |
|
328 | Session().flush() | |
315 |
|
329 | |||
316 | repo_group_data = repo_group.get_api_data() |
|
330 | repo_group_data = repo_group.get_api_data() | |
317 | audit_logger.store_web( |
|
331 | audit_logger.store_web( | |
318 | 'repo_group.create', action_data={'data': repo_group_data}, |
|
332 | 'repo_group.create', action_data={'data': repo_group_data}, | |
319 | user=self._rhodecode_user) |
|
333 | user=self._rhodecode_user) | |
320 |
|
334 | |||
321 | Session().commit() |
|
335 | Session().commit() | |
322 |
|
336 | |||
323 | _new_group_name = form_result['group_name_full'] |
|
337 | _new_group_name = form_result['group_name_full'] | |
324 |
|
338 | |||
325 | repo_group_url = h.link_to( |
|
339 | repo_group_url = h.link_to( | |
326 | _new_group_name, |
|
340 | _new_group_name, | |
327 | h.route_path('repo_group_home', repo_group_name=_new_group_name)) |
|
341 | h.route_path('repo_group_home', repo_group_name=_new_group_name)) | |
328 | h.flash(h.literal(_('Created repository group %s') |
|
342 | h.flash(h.literal(_('Created repository group %s') | |
329 | % repo_group_url), category='success') |
|
343 | % repo_group_url), category='success') | |
330 |
|
344 | |||
331 | except formencode.Invalid as errors: |
|
345 | except formencode.Invalid as errors: | |
332 | data = render( |
|
346 | data = render( | |
333 | 'rhodecode:templates/admin/repo_groups/repo_group_add.mako', |
|
347 | 'rhodecode:templates/admin/repo_groups/repo_group_add.mako', | |
334 | self._get_template_context(c), self.request) |
|
348 | self._get_template_context(c), self.request) | |
335 | html = formencode.htmlfill.render( |
|
349 | html = formencode.htmlfill.render( | |
336 | data, |
|
350 | data, | |
337 | defaults=errors.value, |
|
351 | defaults=errors.value, | |
338 | errors=errors.error_dict or {}, |
|
352 | errors=errors.error_dict or {}, | |
339 | prefix_error=False, |
|
353 | prefix_error=False, | |
340 | encoding="UTF-8", |
|
354 | encoding="UTF-8", | |
341 | force_defaults=False |
|
355 | force_defaults=False | |
342 | ) |
|
356 | ) | |
343 | return Response(html) |
|
357 | return Response(html) | |
344 | except Exception: |
|
358 | except Exception: | |
345 | log.exception("Exception during creation of repository group") |
|
359 | log.exception("Exception during creation of repository group") | |
346 | h.flash(_('Error occurred during creation of repository group %s') |
|
360 | h.flash(_('Error occurred during creation of repository group %s') | |
347 | % repo_group_name, category='error') |
|
361 | % repo_group_name, category='error') | |
348 | raise HTTPFound(h.route_path('home')) |
|
362 | raise HTTPFound(h.route_path('home')) | |
349 |
|
363 | |||
350 | affected_user_ids = [self._rhodecode_user.user_id] |
|
364 | affected_user_ids = [self._rhodecode_user.user_id] | |
351 | if copy_permissions: |
|
365 | if copy_permissions: | |
352 | user_group_perms = repo_group.permissions(expand_from_user_groups=True) |
|
366 | user_group_perms = repo_group.permissions(expand_from_user_groups=True) | |
353 | copy_perms = [perm['user_id'] for perm in user_group_perms] |
|
367 | copy_perms = [perm['user_id'] for perm in user_group_perms] | |
354 | # also include those newly created by copy |
|
368 | # also include those newly created by copy | |
355 | affected_user_ids.extend(copy_perms) |
|
369 | affected_user_ids.extend(copy_perms) | |
356 | PermissionModel().trigger_permission_flush(affected_user_ids) |
|
370 | PermissionModel().trigger_permission_flush(affected_user_ids) | |
357 |
|
371 | |||
358 | raise HTTPFound( |
|
372 | raise HTTPFound( | |
359 | h.route_path('repo_group_home', |
|
373 | h.route_path('repo_group_home', | |
360 | repo_group_name=form_result['group_name_full'])) |
|
374 | repo_group_name=form_result['group_name_full'])) |
@@ -1,266 +1,266 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 |
|
2 | |||
3 | # Copyright (C) 2016-2020 RhodeCode GmbH |
|
3 | # Copyright (C) 2016-2020 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 formencode |
|
22 | import formencode | |
23 | import formencode.htmlfill |
|
23 | import formencode.htmlfill | |
24 |
|
24 | |||
25 | from pyramid.httpexceptions import HTTPFound, HTTPForbidden |
|
25 | from pyramid.httpexceptions import HTTPFound, HTTPForbidden | |
26 | from pyramid.view import view_config |
|
26 | from pyramid.view import view_config | |
27 | from pyramid.renderers import render |
|
27 | from pyramid.renderers import render | |
28 | from pyramid.response import Response |
|
28 | from pyramid.response import Response | |
29 |
|
29 | |||
30 | from rhodecode import events |
|
30 | from rhodecode import events | |
31 | from rhodecode.apps._base import BaseAppView, DataGridAppView |
|
31 | from rhodecode.apps._base import BaseAppView, DataGridAppView | |
32 | from rhodecode.lib.celerylib.utils import get_task_id |
|
32 | from rhodecode.lib.celerylib.utils import get_task_id | |
33 |
|
33 | |||
34 | from rhodecode.lib.auth import ( |
|
34 | from rhodecode.lib.auth import ( | |
35 | LoginRequired, CSRFRequired, NotAnonymous, |
|
35 | LoginRequired, CSRFRequired, NotAnonymous, | |
36 | HasPermissionAny, HasRepoGroupPermissionAny) |
|
36 | HasPermissionAny, HasRepoGroupPermissionAny) | |
37 | from rhodecode.lib import helpers as h |
|
37 | from rhodecode.lib import helpers as h | |
38 | from rhodecode.lib.utils import repo_name_slug |
|
38 | from rhodecode.lib.utils import repo_name_slug | |
39 | from rhodecode.lib.utils2 import safe_int, safe_unicode |
|
39 | from rhodecode.lib.utils2 import safe_int, safe_unicode | |
40 | from rhodecode.model.forms import RepoForm |
|
40 | from rhodecode.model.forms import RepoForm | |
41 | from rhodecode.model.permission import PermissionModel |
|
41 | from rhodecode.model.permission import PermissionModel | |
42 | from rhodecode.model.repo import RepoModel |
|
42 | from rhodecode.model.repo import RepoModel | |
43 | from rhodecode.model.scm import RepoList, RepoGroupList, ScmModel |
|
43 | from rhodecode.model.scm import RepoList, RepoGroupList, ScmModel | |
44 | from rhodecode.model.settings import SettingsModel |
|
44 | from rhodecode.model.settings import SettingsModel | |
45 | from rhodecode.model.db import ( |
|
45 | from rhodecode.model.db import ( | |
46 | in_filter_generator, or_, func, Session, Repository, RepoGroup, User) |
|
46 | in_filter_generator, or_, func, Session, Repository, RepoGroup, User) | |
47 |
|
47 | |||
48 | log = logging.getLogger(__name__) |
|
48 | log = logging.getLogger(__name__) | |
49 |
|
49 | |||
50 |
|
50 | |||
51 | class AdminReposView(BaseAppView, DataGridAppView): |
|
51 | class AdminReposView(BaseAppView, DataGridAppView): | |
52 |
|
52 | |||
53 | def load_default_context(self): |
|
53 | def load_default_context(self): | |
54 | c = self._get_local_tmpl_context() |
|
54 | c = self._get_local_tmpl_context() | |
55 |
|
55 | |||
56 | return c |
|
56 | return c | |
57 |
|
57 | |||
58 | def _load_form_data(self, c): |
|
58 | def _load_form_data(self, c): | |
59 | acl_groups = RepoGroupList(RepoGroup.query().all(), |
|
59 | acl_groups = RepoGroupList(RepoGroup.query().all(), | |
60 | perm_set=['group.write', 'group.admin']) |
|
60 | perm_set=['group.write', 'group.admin']) | |
61 | c.repo_groups = RepoGroup.groups_choices(groups=acl_groups) |
|
61 | c.repo_groups = RepoGroup.groups_choices(groups=acl_groups) | |
62 | c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups) |
|
62 | c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups) | |
63 | c.personal_repo_group = self._rhodecode_user.personal_repo_group |
|
63 | c.personal_repo_group = self._rhodecode_user.personal_repo_group | |
64 |
|
64 | |||
65 | @LoginRequired() |
|
65 | @LoginRequired() | |
66 | @NotAnonymous() |
|
66 | @NotAnonymous() | |
67 | # perms check inside |
|
67 | # perms check inside | |
68 | @view_config( |
|
68 | @view_config( | |
69 | route_name='repos', request_method='GET', |
|
69 | route_name='repos', request_method='GET', | |
70 | renderer='rhodecode:templates/admin/repos/repos.mako') |
|
70 | renderer='rhodecode:templates/admin/repos/repos.mako') | |
71 | def repository_list(self): |
|
71 | def repository_list(self): | |
72 | c = self.load_default_context() |
|
72 | c = self.load_default_context() | |
73 | return self._get_template_context(c) |
|
73 | return self._get_template_context(c) | |
74 |
|
74 | |||
75 | @LoginRequired() |
|
75 | @LoginRequired() | |
76 | @NotAnonymous() |
|
76 | @NotAnonymous() | |
77 | # perms check inside |
|
77 | # perms check inside | |
78 | @view_config( |
|
78 | @view_config( | |
79 | route_name='repos_data', request_method='GET', |
|
79 | route_name='repos_data', request_method='GET', | |
80 | renderer='json_ext', xhr=True) |
|
80 | renderer='json_ext', xhr=True) | |
81 | def repository_list_data(self): |
|
81 | def repository_list_data(self): | |
82 | self.load_default_context() |
|
82 | self.load_default_context() | |
83 | column_map = { |
|
83 | column_map = { | |
84 | 'name': 'repo_name', |
|
84 | 'name': 'repo_name', | |
85 | 'desc': 'description', |
|
85 | 'desc': 'description', | |
86 | 'last_change': 'updated_on', |
|
86 | 'last_change': 'updated_on', | |
87 | 'owner': 'user_username', |
|
87 | 'owner': 'user_username', | |
88 | } |
|
88 | } | |
89 | draw, start, limit = self._extract_chunk(self.request) |
|
89 | draw, start, limit = self._extract_chunk(self.request) | |
90 | search_q, order_by, order_dir = self._extract_ordering( |
|
90 | search_q, order_by, order_dir = self._extract_ordering( | |
91 | self.request, column_map=column_map) |
|
91 | self.request, column_map=column_map) | |
92 |
|
92 | |||
93 | _perms = ['repository.admin'] |
|
93 | _perms = ['repository.admin'] | |
94 | allowed_ids = [-1] + self._rhodecode_user.repo_acl_ids_from_stack(_perms) |
|
94 | allowed_ids = [-1] + self._rhodecode_user.repo_acl_ids_from_stack(_perms) | |
95 |
|
95 | |||
96 | repos_data_total_count = Repository.query() \ |
|
96 | repos_data_total_count = Repository.query() \ | |
97 | .filter(or_( |
|
97 | .filter(or_( | |
98 | # generate multiple IN to fix limitation problems |
|
98 | # generate multiple IN to fix limitation problems | |
99 | *in_filter_generator(Repository.repo_id, allowed_ids)) |
|
99 | *in_filter_generator(Repository.repo_id, allowed_ids)) | |
100 | ) \ |
|
100 | ) \ | |
101 | .count() |
|
101 | .count() | |
102 |
|
102 | |||
103 | base_q = Session.query( |
|
103 | base_q = Session.query( | |
104 | Repository.repo_id, |
|
104 | Repository.repo_id, | |
105 | Repository.repo_name, |
|
105 | Repository.repo_name, | |
106 | Repository.description, |
|
106 | Repository.description, | |
107 | Repository.repo_type, |
|
107 | Repository.repo_type, | |
108 | Repository.repo_state, |
|
108 | Repository.repo_state, | |
109 | Repository.private, |
|
109 | Repository.private, | |
110 | Repository.archived, |
|
110 | Repository.archived, | |
111 | Repository.fork, |
|
111 | Repository.fork, | |
112 | Repository.updated_on, |
|
112 | Repository.updated_on, | |
113 | Repository._changeset_cache, |
|
113 | Repository._changeset_cache, | |
114 | User, |
|
114 | User, | |
115 | ) \ |
|
115 | ) \ | |
116 | .filter(or_( |
|
116 | .filter(or_( | |
117 | # generate multiple IN to fix limitation problems |
|
117 | # generate multiple IN to fix limitation problems | |
118 | *in_filter_generator(Repository.repo_id, allowed_ids)) |
|
118 | *in_filter_generator(Repository.repo_id, allowed_ids)) | |
119 | ) \ |
|
119 | ) \ | |
120 | .join(User, User.user_id == Repository.user_id) \ |
|
120 | .join(User, User.user_id == Repository.user_id) \ | |
121 | .group_by(Repository, User) |
|
121 | .group_by(Repository, User) | |
122 |
|
122 | |||
123 | if search_q: |
|
123 | if search_q: | |
124 | like_expression = u'%{}%'.format(safe_unicode(search_q)) |
|
124 | like_expression = u'%{}%'.format(safe_unicode(search_q)) | |
125 | base_q = base_q.filter(or_( |
|
125 | base_q = base_q.filter(or_( | |
126 | Repository.repo_name.ilike(like_expression), |
|
126 | Repository.repo_name.ilike(like_expression), | |
127 | )) |
|
127 | )) | |
128 |
|
128 | |||
129 | repos_data_total_filtered_count = base_q.count() |
|
129 | repos_data_total_filtered_count = base_q.count() | |
130 |
|
130 | |||
131 | sort_defined = False |
|
131 | sort_defined = False | |
132 | if order_by == 'repo_name': |
|
132 | if order_by == 'repo_name': | |
133 | sort_col = func.lower(Repository.repo_name) |
|
133 | sort_col = func.lower(Repository.repo_name) | |
134 | sort_defined = True |
|
134 | sort_defined = True | |
135 | elif order_by == 'user_username': |
|
135 | elif order_by == 'user_username': | |
136 | sort_col = User.username |
|
136 | sort_col = User.username | |
137 | else: |
|
137 | else: | |
138 | sort_col = getattr(Repository, order_by, None) |
|
138 | sort_col = getattr(Repository, order_by, None) | |
139 |
|
139 | |||
140 | if sort_defined or sort_col: |
|
140 | if sort_defined or sort_col: | |
141 | if order_dir == 'asc': |
|
141 | if order_dir == 'asc': | |
142 | sort_col = sort_col.asc() |
|
142 | sort_col = sort_col.asc() | |
143 | else: |
|
143 | else: | |
144 | sort_col = sort_col.desc() |
|
144 | sort_col = sort_col.desc() | |
145 |
|
145 | |||
146 | base_q = base_q.order_by(sort_col) |
|
146 | base_q = base_q.order_by(sort_col) | |
147 | base_q = base_q.offset(start).limit(limit) |
|
147 | base_q = base_q.offset(start).limit(limit) | |
148 |
|
148 | |||
149 | repos_list = base_q.all() |
|
149 | repos_list = base_q.all() | |
150 |
|
150 | |||
151 | repos_data = RepoModel().get_repos_as_dict( |
|
151 | repos_data = RepoModel().get_repos_as_dict( | |
152 | repo_list=repos_list, admin=True, super_user_actions=True) |
|
152 | repo_list=repos_list, admin=True, super_user_actions=True) | |
153 |
|
153 | |||
154 | data = ({ |
|
154 | data = ({ | |
155 | 'draw': draw, |
|
155 | 'draw': draw, | |
156 | 'data': repos_data, |
|
156 | 'data': repos_data, | |
157 | 'recordsTotal': repos_data_total_count, |
|
157 | 'recordsTotal': repos_data_total_count, | |
158 | 'recordsFiltered': repos_data_total_filtered_count, |
|
158 | 'recordsFiltered': repos_data_total_filtered_count, | |
159 | }) |
|
159 | }) | |
160 | return data |
|
160 | return data | |
161 |
|
161 | |||
162 | @LoginRequired() |
|
162 | @LoginRequired() | |
163 | @NotAnonymous() |
|
163 | @NotAnonymous() | |
164 | # perms check inside |
|
164 | # perms check inside | |
165 | @view_config( |
|
165 | @view_config( | |
166 | route_name='repo_new', request_method='GET', |
|
166 | route_name='repo_new', request_method='GET', | |
167 | renderer='rhodecode:templates/admin/repos/repo_add.mako') |
|
167 | renderer='rhodecode:templates/admin/repos/repo_add.mako') | |
168 | def repository_new(self): |
|
168 | def repository_new(self): | |
169 | c = self.load_default_context() |
|
169 | c = self.load_default_context() | |
170 |
|
170 | |||
171 | new_repo = self.request.GET.get('repo', '') |
|
171 | new_repo = self.request.GET.get('repo', '') | |
172 | parent_group = safe_int(self.request.GET.get('parent_group')) |
|
172 | parent_group_id = safe_int(self.request.GET.get('parent_group')) | |
173 | _gr = RepoGroup.get(parent_group) |
|
173 | _gr = RepoGroup.get(parent_group_id) | |
174 |
|
174 | |||
175 | if not HasPermissionAny('hg.admin', 'hg.create.repository')(): |
|
175 | if not HasPermissionAny('hg.admin', 'hg.create.repository')(): | |
176 | # you're not super admin nor have global create permissions, |
|
176 | # you're not super admin nor have global create permissions, | |
177 | # but maybe you have at least write permission to a parent group ? |
|
177 | # but maybe you have at least write permission to a parent group ? | |
178 |
|
178 | |||
179 | gr_name = _gr.group_name if _gr else None |
|
179 | gr_name = _gr.group_name if _gr else None | |
180 | # create repositories with write permission on group is set to true |
|
180 | # create repositories with write permission on group is set to true | |
181 | create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')() |
|
181 | create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')() | |
182 | group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name) |
|
182 | group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name) | |
183 | group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name) |
|
183 | group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name) | |
184 | if not (group_admin or (group_write and create_on_write)): |
|
184 | if not (group_admin or (group_write and create_on_write)): | |
185 | raise HTTPForbidden() |
|
185 | raise HTTPForbidden() | |
186 |
|
186 | |||
187 | self._load_form_data(c) |
|
187 | self._load_form_data(c) | |
188 | c.new_repo = repo_name_slug(new_repo) |
|
188 | c.new_repo = repo_name_slug(new_repo) | |
189 |
|
189 | |||
190 | # apply the defaults from defaults page |
|
190 | # apply the defaults from defaults page | |
191 | defaults = SettingsModel().get_default_repo_settings(strip_prefix=True) |
|
191 | defaults = SettingsModel().get_default_repo_settings(strip_prefix=True) | |
192 | # set checkbox to autochecked |
|
192 | # set checkbox to autochecked | |
193 | defaults['repo_copy_permissions'] = True |
|
193 | defaults['repo_copy_permissions'] = True | |
194 |
|
194 | |||
195 | parent_group_choice = '-1' |
|
195 | parent_group_choice = '-1' | |
196 | if not self._rhodecode_user.is_admin and self._rhodecode_user.personal_repo_group: |
|
196 | if not self._rhodecode_user.is_admin and self._rhodecode_user.personal_repo_group: | |
197 | parent_group_choice = self._rhodecode_user.personal_repo_group |
|
197 | parent_group_choice = self._rhodecode_user.personal_repo_group | |
198 |
|
198 | |||
199 | if parent_group and _gr: |
|
199 | if parent_group_id and _gr: | |
200 | if parent_group in [x[0] for x in c.repo_groups]: |
|
200 | if parent_group_id in [x[0] for x in c.repo_groups]: | |
201 | parent_group_choice = safe_unicode(parent_group) |
|
201 | parent_group_choice = safe_unicode(parent_group_id) | |
202 |
|
202 | |||
203 | defaults.update({'repo_group': parent_group_choice}) |
|
203 | defaults.update({'repo_group': parent_group_choice}) | |
204 |
|
204 | |||
205 | data = render('rhodecode:templates/admin/repos/repo_add.mako', |
|
205 | data = render('rhodecode:templates/admin/repos/repo_add.mako', | |
206 | self._get_template_context(c), self.request) |
|
206 | self._get_template_context(c), self.request) | |
207 | html = formencode.htmlfill.render( |
|
207 | html = formencode.htmlfill.render( | |
208 | data, |
|
208 | data, | |
209 | defaults=defaults, |
|
209 | defaults=defaults, | |
210 | encoding="UTF-8", |
|
210 | encoding="UTF-8", | |
211 | force_defaults=False |
|
211 | force_defaults=False | |
212 | ) |
|
212 | ) | |
213 | return Response(html) |
|
213 | return Response(html) | |
214 |
|
214 | |||
215 | @LoginRequired() |
|
215 | @LoginRequired() | |
216 | @NotAnonymous() |
|
216 | @NotAnonymous() | |
217 | @CSRFRequired() |
|
217 | @CSRFRequired() | |
218 | # perms check inside |
|
218 | # perms check inside | |
219 | @view_config( |
|
219 | @view_config( | |
220 | route_name='repo_create', request_method='POST', |
|
220 | route_name='repo_create', request_method='POST', | |
221 | renderer='rhodecode:templates/admin/repos/repos.mako') |
|
221 | renderer='rhodecode:templates/admin/repos/repos.mako') | |
222 | def repository_create(self): |
|
222 | def repository_create(self): | |
223 | c = self.load_default_context() |
|
223 | c = self.load_default_context() | |
224 |
|
224 | |||
225 | form_result = {} |
|
225 | form_result = {} | |
226 | self._load_form_data(c) |
|
226 | self._load_form_data(c) | |
227 |
|
227 | |||
228 | try: |
|
228 | try: | |
229 | # CanWriteToGroup validators checks permissions of this POST |
|
229 | # CanWriteToGroup validators checks permissions of this POST | |
230 | form = RepoForm( |
|
230 | form = RepoForm( | |
231 | self.request.translate, repo_groups=c.repo_groups_choices)() |
|
231 | self.request.translate, repo_groups=c.repo_groups_choices)() | |
232 | form_result = form.to_python(dict(self.request.POST)) |
|
232 | form_result = form.to_python(dict(self.request.POST)) | |
233 | copy_permissions = form_result.get('repo_copy_permissions') |
|
233 | copy_permissions = form_result.get('repo_copy_permissions') | |
234 | # create is done sometimes async on celery, db transaction |
|
234 | # create is done sometimes async on celery, db transaction | |
235 | # management is handled there. |
|
235 | # management is handled there. | |
236 | task = RepoModel().create(form_result, self._rhodecode_user.user_id) |
|
236 | task = RepoModel().create(form_result, self._rhodecode_user.user_id) | |
237 | task_id = get_task_id(task) |
|
237 | task_id = get_task_id(task) | |
238 | except formencode.Invalid as errors: |
|
238 | except formencode.Invalid as errors: | |
239 | data = render('rhodecode:templates/admin/repos/repo_add.mako', |
|
239 | data = render('rhodecode:templates/admin/repos/repo_add.mako', | |
240 | self._get_template_context(c), self.request) |
|
240 | self._get_template_context(c), self.request) | |
241 | html = formencode.htmlfill.render( |
|
241 | html = formencode.htmlfill.render( | |
242 | data, |
|
242 | data, | |
243 | defaults=errors.value, |
|
243 | defaults=errors.value, | |
244 | errors=errors.error_dict or {}, |
|
244 | errors=errors.error_dict or {}, | |
245 | prefix_error=False, |
|
245 | prefix_error=False, | |
246 | encoding="UTF-8", |
|
246 | encoding="UTF-8", | |
247 | force_defaults=False |
|
247 | force_defaults=False | |
248 | ) |
|
248 | ) | |
249 | return Response(html) |
|
249 | return Response(html) | |
250 |
|
250 | |||
251 | except Exception as e: |
|
251 | except Exception as e: | |
252 | msg = self._log_creation_exception(e, form_result.get('repo_name')) |
|
252 | msg = self._log_creation_exception(e, form_result.get('repo_name')) | |
253 | h.flash(msg, category='error') |
|
253 | h.flash(msg, category='error') | |
254 | raise HTTPFound(h.route_path('home')) |
|
254 | raise HTTPFound(h.route_path('home')) | |
255 |
|
255 | |||
256 | repo_name = form_result.get('repo_name_full') |
|
256 | repo_name = form_result.get('repo_name_full') | |
257 |
|
257 | |||
258 | affected_user_ids = [self._rhodecode_user.user_id] |
|
258 | affected_user_ids = [self._rhodecode_user.user_id] | |
259 | if copy_permissions: |
|
259 | if copy_permissions: | |
260 | # permission flush is done in repo creating |
|
260 | # permission flush is done in repo creating | |
261 | pass |
|
261 | pass | |
262 | PermissionModel().trigger_permission_flush(affected_user_ids) |
|
262 | PermissionModel().trigger_permission_flush(affected_user_ids) | |
263 |
|
263 | |||
264 | raise HTTPFound( |
|
264 | raise HTTPFound( | |
265 | h.route_path('repo_creating', repo_name=repo_name, |
|
265 | h.route_path('repo_creating', repo_name=repo_name, | |
266 | _query=dict(task_id=task_id))) |
|
266 | _query=dict(task_id=task_id))) |
@@ -1,111 +1,122 b'' | |||||
1 | ## -*- coding: utf-8 -*- |
|
1 | ## -*- coding: utf-8 -*- | |
2 | <%inherit file="/base/base.mako"/> |
|
2 | <%inherit file="/base/base.mako"/> | |
3 |
|
3 | |||
4 | <%def name="title()"> |
|
4 | <%def name="title()"> | |
5 | ${_('Add repository group')} |
|
5 | ${_('Add repository group')} | |
6 | %if c.rhodecode_name: |
|
6 | %if c.rhodecode_name: | |
7 | · ${h.branding(c.rhodecode_name)} |
|
7 | · ${h.branding(c.rhodecode_name)} | |
8 | %endif |
|
8 | %endif | |
9 | </%def> |
|
9 | </%def> | |
10 |
|
10 | |||
11 | <%def name="breadcrumbs_links()"> |
|
11 | <%def name="breadcrumbs_links()"> | |
12 | ${h.link_to(_('Admin'),h.route_path('admin_home'))} |
|
12 | ${h.link_to(_('Admin'),h.route_path('admin_home'))} | |
13 | » |
|
13 | » | |
14 | ${h.link_to(_('Repository groups'),h.route_path('repo_groups'))} |
|
14 | ${h.link_to(_('Repository groups'),h.route_path('repo_groups'))} | |
15 | » |
|
15 | » | |
16 | ${_('Add Repository Group')} |
|
16 | ${_('Add Repository Group')} | |
17 | </%def> |
|
17 | </%def> | |
18 |
|
18 | |||
19 | <%def name="menu_bar_nav()"> |
|
19 | <%def name="menu_bar_nav()"> | |
20 | ${self.menu_items(active='admin')} |
|
20 | ${self.menu_items(active='admin')} | |
21 | </%def> |
|
21 | </%def> | |
22 |
|
22 | |||
23 | <%def name="menu_bar_subnav()"> |
|
23 | <%def name="menu_bar_subnav()"> | |
24 | ${self.admin_menu(active='repository_groups')} |
|
24 | ${self.admin_menu(active='repository_groups')} | |
25 | </%def> |
|
25 | </%def> | |
26 |
|
26 | |||
27 | <%def name="main()"> |
|
27 | <%def name="main()"> | |
28 | <div class="box"> |
|
28 | <div class="box"> | |
29 | ${h.secure_form(h.route_path('repo_group_create'), request=request)} |
|
29 | ${h.secure_form(h.route_path('repo_group_create'), request=request)} | |
30 | <div class="form"> |
|
30 | <div class="form"> | |
31 | <!-- fields --> |
|
31 | <!-- fields --> | |
32 | <div class="fields"> |
|
32 | <div class="fields"> | |
33 | <div class="field"> |
|
33 | <div class="field"> | |
34 | <div class="label"> |
|
34 | <div class="label"> | |
35 | <label for="group_name">${_('Group name')}:</label> |
|
35 | <label for="group_name">${_('Group name')}:</label> | |
36 | </div> |
|
36 | </div> | |
37 | <div class="input"> |
|
37 | <div class="input"> | |
38 | ${h.text('group_name', class_="medium")} |
|
38 | ${h.text('group_name', class_="medium")} | |
39 | </div> |
|
39 | </div> | |
40 | </div> |
|
40 | </div> | |
41 |
|
41 | |||
42 | <div class="field"> |
|
42 | <div class="field"> | |
43 | <div class="label"> |
|
43 | <div class="label"> | |
44 | <label for="group_parent_id">${_('Repository group')}:</label> |
|
44 | <label for="group_parent_id">${_('Repository group')}:</label> | |
45 | </div> |
|
45 | </div> | |
46 | <div class="select"> |
|
46 | <div class="select"> | |
47 | ${h.select('group_parent_id',request.GET.get('parent_group'),c.repo_groups,class_="medium")} |
|
47 | ${h.select('group_parent_id', request.GET.get('parent_group'),c.repo_groups,class_="medium")} | |
|
48 | % if c.personal_repo_group: | |||
|
49 | <a class="btn" href="#" id="select_my_group" data-personal-group-id="${c.personal_repo_group.group_id}"> | |||
|
50 | ${_('Select my personal group ({})').format(c.personal_repo_group.group_name)} | |||
|
51 | </a> | |||
|
52 | % endif | |||
48 | </div> |
|
53 | </div> | |
49 | </div> |
|
54 | </div> | |
50 |
|
55 | |||
51 | <div class="field"> |
|
56 | <div class="field"> | |
52 | <div class="label"> |
|
57 | <div class="label"> | |
53 | <label for="group_description">${_('Description')}:</label> |
|
58 | <label for="group_description">${_('Description')}:</label> | |
54 | </div> |
|
59 | </div> | |
55 | <div class="textarea editor"> |
|
60 | <div class="textarea editor"> | |
56 | ${h.textarea('group_description',cols=23,rows=5,class_="medium")} |
|
61 | ${h.textarea('group_description',cols=23,rows=5,class_="medium")} | |
57 | <% metatags_url = h.literal('''<a href="#metatagsShow" onclick="$('#meta-tags-desc').toggle();return false">meta-tags</a>''') %> |
|
62 | <% metatags_url = h.literal('''<a href="#metatagsShow" onclick="$('#meta-tags-desc').toggle();return false">meta-tags</a>''') %> | |
58 | <span class="help-block"> |
|
63 | <span class="help-block"> | |
59 | % if c.visual.stylify_metatags: |
|
64 | % if c.visual.stylify_metatags: | |
60 | ${_('Plain text format with {metatags} support.').format(metatags=metatags_url)|n} |
|
65 | ${_('Plain text format with {metatags} support.').format(metatags=metatags_url)|n} | |
61 | % else: |
|
66 | % else: | |
62 | ${_('Plain text format.')} |
|
67 | ${_('Plain text format.')} | |
63 | % endif |
|
68 | % endif | |
64 | </span> |
|
69 | </span> | |
65 | <span id="meta-tags-desc" style="display: none"> |
|
70 | <span id="meta-tags-desc" style="display: none"> | |
66 | <%namespace name="dt" file="/data_table/_dt_elements.mako"/> |
|
71 | <%namespace name="dt" file="/data_table/_dt_elements.mako"/> | |
67 | ${dt.metatags_help()} |
|
72 | ${dt.metatags_help()} | |
68 | </span> |
|
73 | </span> | |
69 | </div> |
|
74 | </div> | |
70 | </div> |
|
75 | </div> | |
71 |
|
76 | |||
72 | <div id="copy_perms" class="field"> |
|
77 | <div id="copy_perms" class="field"> | |
73 | <div class="label label-checkbox"> |
|
78 | <div class="label label-checkbox"> | |
74 | <label for="group_copy_permissions">${_('Copy Parent Group Permissions')}:</label> |
|
79 | <label for="group_copy_permissions">${_('Copy Parent Group Permissions')}:</label> | |
75 | </div> |
|
80 | </div> | |
76 | <div class="checkboxes"> |
|
81 | <div class="checkboxes"> | |
77 | ${h.checkbox('group_copy_permissions', value="True", checked="checked")} |
|
82 | ${h.checkbox('group_copy_permissions', value="True", checked="checked")} | |
78 | <span class="help-block">${_('Copy permissions from parent repository group.')}</span> |
|
83 | <span class="help-block">${_('Copy permissions from parent repository group.')}</span> | |
79 | </div> |
|
84 | </div> | |
80 | </div> |
|
85 | </div> | |
81 |
|
86 | |||
82 | <div class="buttons"> |
|
87 | <div class="buttons"> | |
83 | ${h.submit('save',_('Create Repository Group'),class_="btn")} |
|
88 | ${h.submit('save',_('Create Repository Group'),class_="btn")} | |
84 | </div> |
|
89 | </div> | |
85 | </div> |
|
90 | </div> | |
86 | </div> |
|
91 | </div> | |
87 | ${h.end_form()} |
|
92 | ${h.end_form()} | |
88 | </div> |
|
93 | </div> | |
89 | <script> |
|
94 | <script> | |
90 | $(document).ready(function(){ |
|
95 | $(document).ready(function(){ | |
91 | var setCopyPermsOption = function(group_val){ |
|
96 | var setCopyPermsOption = function(group_val){ | |
92 | if(group_val !== "-1"){ |
|
97 | if(group_val !== "-1"){ | |
93 | $('#copy_perms').show() |
|
98 | $('#copy_perms').show() | |
94 | } |
|
99 | } | |
95 | else{ |
|
100 | else{ | |
96 | $('#copy_perms').hide(); |
|
101 | $('#copy_perms').hide(); | |
97 | } |
|
102 | } | |
98 | }; |
|
103 | }; | |
99 | $("#group_parent_id").select2({ |
|
104 | $("#group_parent_id").select2({ | |
100 | 'containerCssClass': "drop-menu", |
|
105 | 'containerCssClass': "drop-menu", | |
101 | 'dropdownCssClass': "drop-menu-dropdown", |
|
106 | 'dropdownCssClass': "drop-menu-dropdown", | |
102 | 'dropdownAutoWidth': true |
|
107 | 'dropdownAutoWidth': true | |
103 | }); |
|
108 | }); | |
104 | setCopyPermsOption($('#group_parent_id').val()); |
|
109 | setCopyPermsOption($('#group_parent_id').val()); | |
105 | $("#group_parent_id").on("change", function(e) { |
|
110 | $("#group_parent_id").on("change", function(e) { | |
106 | setCopyPermsOption(e.val) |
|
111 | setCopyPermsOption(e.val) | |
107 | }); |
|
112 | }); | |
108 | $('#group_name').focus(); |
|
113 | $('#group_name').focus(); | |
|
114 | ||||
|
115 | $('#select_my_group').on('click', function(e){ | |||
|
116 | e.preventDefault(); | |||
|
117 | $("#group_parent_id").val($(this).data('personalGroupId')).trigger("change"); | |||
|
118 | }) | |||
|
119 | ||||
109 | }) |
|
120 | }) | |
110 | </script> |
|
121 | </script> | |
111 | </%def> |
|
122 | </%def> |
@@ -1,169 +1,169 b'' | |||||
1 | ## -*- coding: utf-8 -*- |
|
1 | ## -*- coding: utf-8 -*- | |
2 |
|
2 | |||
3 | ${h.secure_form(h.route_path('repo_create'), request=request)} |
|
3 | ${h.secure_form(h.route_path('repo_create'), request=request)} | |
4 | <div class="form"> |
|
4 | <div class="form"> | |
5 | <!-- fields --> |
|
5 | <!-- fields --> | |
6 | <div class="fields"> |
|
6 | <div class="fields"> | |
7 | <div class="field"> |
|
7 | <div class="field"> | |
8 | <div class="label"> |
|
8 | <div class="label"> | |
9 | <label for="repo_name">${_('Repository name')}:</label> |
|
9 | <label for="repo_name">${_('Repository name')}:</label> | |
10 | </div> |
|
10 | </div> | |
11 | <div class="input"> |
|
11 | <div class="input"> | |
12 | ${h.text('repo_name', class_="medium")} |
|
12 | ${h.text('repo_name', class_="medium")} | |
13 | <div class="info-block"> |
|
13 | <div class="info-block"> | |
14 | <a id="remote_clone_toggle" href="#">${_('Import Existing Repository ?')}</a> |
|
14 | <a id="remote_clone_toggle" href="#">${_('Import Existing Repository ?')}</a> | |
15 | </div> |
|
15 | </div> | |
16 | %if not c.rhodecode_user.is_admin: |
|
16 | %if not c.rhodecode_user.is_admin: | |
17 | ${h.hidden('user_created',True)} |
|
17 | ${h.hidden('user_created',True)} | |
18 | %endif |
|
18 | %endif | |
19 | </div> |
|
19 | </div> | |
20 | </div> |
|
20 | </div> | |
21 | <div id="remote_clone" class="field" style="display: none;"> |
|
21 | <div id="remote_clone" class="field" style="display: none;"> | |
22 | <div class="label"> |
|
22 | <div class="label"> | |
23 | <label for="clone_uri">${_('Clone from')}:</label> |
|
23 | <label for="clone_uri">${_('Clone from')}:</label> | |
24 | </div> |
|
24 | </div> | |
25 | <div class="input"> |
|
25 | <div class="input"> | |
26 | ${h.text('clone_uri', class_="medium")} |
|
26 | ${h.text('clone_uri', class_="medium")} | |
27 | <span class="help-block"> |
|
27 | <span class="help-block"> | |
28 | <pre> |
|
28 | <pre> | |
29 | - The repository must be accessible over http:// or https:// |
|
29 | - The repository must be accessible over http:// or https:// | |
30 | - For Git projects it's recommended appending .git to the end of clone url. |
|
30 | - For Git projects it's recommended appending .git to the end of clone url. | |
31 | - Make sure to select proper repository type from the below selector before importing it. |
|
31 | - Make sure to select proper repository type from the below selector before importing it. | |
32 | - If your HTTP[S] repository is not publicly accessible, |
|
32 | - If your HTTP[S] repository is not publicly accessible, | |
33 | add authentication information to the URL: https://username:password@server.company.com/repo-name. |
|
33 | add authentication information to the URL: https://username:password@server.company.com/repo-name. | |
34 | - The Git LFS/Mercurial Largefiles objects will not be imported. |
|
34 | - The Git LFS/Mercurial Largefiles objects will not be imported. | |
35 | - For very large repositories, it's recommended to manually copy them into the |
|
35 | - For very large repositories, it's recommended to manually copy them into the | |
36 | RhodeCode <a href="${h.route_path('admin_settings_vcs', _anchor='vcs-storage-options')}">storage location</a> and run <a href="${h.route_path('admin_settings_mapping')}">Remap and Rescan</a>. |
|
36 | RhodeCode <a href="${h.route_path('admin_settings_vcs', _anchor='vcs-storage-options')}">storage location</a> and run <a href="${h.route_path('admin_settings_mapping')}">Remap and Rescan</a>. | |
37 | </pre> |
|
37 | </pre> | |
38 | </span> |
|
38 | </span> | |
39 | </div> |
|
39 | </div> | |
40 | </div> |
|
40 | </div> | |
41 | <div class="field"> |
|
41 | <div class="field"> | |
42 | <div class="label"> |
|
42 | <div class="label"> | |
43 | <label for="repo_group">${_('Repository group')}:</label> |
|
43 | <label for="repo_group">${_('Repository group')}:</label> | |
44 | </div> |
|
44 | </div> | |
45 | <div class="select"> |
|
45 | <div class="select"> | |
46 | ${h.select('repo_group',request.GET.get('parent_group'),c.repo_groups,class_="medium")} |
|
46 | ${h.select('repo_group',request.GET.get('parent_group'),c.repo_groups,class_="medium")} | |
47 | % if c.personal_repo_group: |
|
47 | % if c.personal_repo_group: | |
48 | <a class="btn" href="#" id="select_my_group" data-personal-group-id="${c.personal_repo_group.group_id}"> |
|
48 | <a class="btn" href="#" id="select_my_group" data-personal-group-id="${c.personal_repo_group.group_id}"> | |
49 |
${_('Select my personal group ( |
|
49 | ${_('Select my personal group ({})').format(c.personal_repo_group.group_name)} | |
50 | </a> |
|
50 | </a> | |
51 | % endif |
|
51 | % endif | |
52 | <span class="help-block">${_('Optionally select a group to put this repository into.')}</span> |
|
52 | <span class="help-block">${_('Optionally select a group to put this repository into.')}</span> | |
53 | </div> |
|
53 | </div> | |
54 | </div> |
|
54 | </div> | |
55 |
|
55 | |||
56 | <div class="field"> |
|
56 | <div class="field"> | |
57 | <div class="label"> |
|
57 | <div class="label"> | |
58 | <label for="repo_type">${_('Type')}:</label> |
|
58 | <label for="repo_type">${_('Type')}:</label> | |
59 | </div> |
|
59 | </div> | |
60 | <div class="fields repo-type-radio"> |
|
60 | <div class="fields repo-type-radio"> | |
61 |
|
61 | |||
62 |
|
62 | |||
63 | % for backend in c.backends: |
|
63 | % for backend in c.backends: | |
64 | % if loop.index == 0: |
|
64 | % if loop.index == 0: | |
65 | <input id="repo_type_${backend}" name="repo_type" type="radio" value="${backend}" checked="checked"/> |
|
65 | <input id="repo_type_${backend}" name="repo_type" type="radio" value="${backend}" checked="checked"/> | |
66 | % else: |
|
66 | % else: | |
67 | <input id="repo_type_${backend}" name="repo_type" type="radio" value="${backend}" /> |
|
67 | <input id="repo_type_${backend}" name="repo_type" type="radio" value="${backend}" /> | |
68 | % endif |
|
68 | % endif | |
69 |
|
69 | |||
70 | <label for="repo_type_${backend}"> |
|
70 | <label for="repo_type_${backend}"> | |
71 | <i class="icon-${backend}" style="font-size: 16px"></i> |
|
71 | <i class="icon-${backend}" style="font-size: 16px"></i> | |
72 | ${backend.upper()} |
|
72 | ${backend.upper()} | |
73 | </label> |
|
73 | </label> | |
74 |
|
74 | |||
75 | % endfor |
|
75 | % endfor | |
76 |
|
76 | |||
77 |
|
77 | |||
78 | <span class="help-block">${_('Set the type of repository to create.')}</span> |
|
78 | <span class="help-block">${_('Set the type of repository to create.')}</span> | |
79 | </div> |
|
79 | </div> | |
80 | </div> |
|
80 | </div> | |
81 | <div class="field"> |
|
81 | <div class="field"> | |
82 | <div class="label"> |
|
82 | <div class="label"> | |
83 | <label for="repo_description">${_('Description')}:</label> |
|
83 | <label for="repo_description">${_('Description')}:</label> | |
84 | </div> |
|
84 | </div> | |
85 | <div class="textarea editor"> |
|
85 | <div class="textarea editor"> | |
86 | ${h.textarea('repo_description',cols=23,rows=5,class_="medium")} |
|
86 | ${h.textarea('repo_description',cols=23,rows=5,class_="medium")} | |
87 | <% metatags_url = h.literal('''<a href="#metatagsShow" onclick="$('#meta-tags-desc').toggle();return false">meta-tags</a>''') %> |
|
87 | <% metatags_url = h.literal('''<a href="#metatagsShow" onclick="$('#meta-tags-desc').toggle();return false">meta-tags</a>''') %> | |
88 | <span class="help-block"> |
|
88 | <span class="help-block"> | |
89 | % if c.visual.stylify_metatags: |
|
89 | % if c.visual.stylify_metatags: | |
90 | ${_('Plain text format with {metatags} support.').format(metatags=metatags_url)|n} |
|
90 | ${_('Plain text format with {metatags} support.').format(metatags=metatags_url)|n} | |
91 | % else: |
|
91 | % else: | |
92 | ${_('Plain text format.')} |
|
92 | ${_('Plain text format.')} | |
93 | % endif |
|
93 | % endif | |
94 | ${_('Add a README file for longer descriptions')} |
|
94 | ${_('Add a README file for longer descriptions')} | |
95 | </span> |
|
95 | </span> | |
96 | <span id="meta-tags-desc" style="display: none"> |
|
96 | <span id="meta-tags-desc" style="display: none"> | |
97 | <%namespace name="dt" file="/data_table/_dt_elements.mako"/> |
|
97 | <%namespace name="dt" file="/data_table/_dt_elements.mako"/> | |
98 | ${dt.metatags_help()} |
|
98 | ${dt.metatags_help()} | |
99 | </span> |
|
99 | </span> | |
100 | </div> |
|
100 | </div> | |
101 | </div> |
|
101 | </div> | |
102 | <div id="copy_perms" class="field"> |
|
102 | <div id="copy_perms" class="field"> | |
103 | <div class="label label-checkbox"> |
|
103 | <div class="label label-checkbox"> | |
104 | <label for="repo_copy_permissions">${_('Copy Parent Group Permissions')}:</label> |
|
104 | <label for="repo_copy_permissions">${_('Copy Parent Group Permissions')}:</label> | |
105 | </div> |
|
105 | </div> | |
106 | <div class="checkboxes"> |
|
106 | <div class="checkboxes"> | |
107 | ${h.checkbox('repo_copy_permissions', value="True", checked="checked")} |
|
107 | ${h.checkbox('repo_copy_permissions', value="True", checked="checked")} | |
108 | <span class="help-block">${_('Copy permissions from parent repository group.')}</span> |
|
108 | <span class="help-block">${_('Copy permissions from parent repository group.')}</span> | |
109 | </div> |
|
109 | </div> | |
110 | </div> |
|
110 | </div> | |
111 | <div class="field"> |
|
111 | <div class="field"> | |
112 | <div class="label label-checkbox"> |
|
112 | <div class="label label-checkbox"> | |
113 | <label for="repo_private">${_('Private Repository')}:</label> |
|
113 | <label for="repo_private">${_('Private Repository')}:</label> | |
114 | </div> |
|
114 | </div> | |
115 | <div class="checkboxes"> |
|
115 | <div class="checkboxes"> | |
116 | ${h.checkbox('repo_private',value="True")} |
|
116 | ${h.checkbox('repo_private',value="True")} | |
117 | <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span> |
|
117 | <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span> | |
118 | </div> |
|
118 | </div> | |
119 | </div> |
|
119 | </div> | |
120 | <div class="buttons"> |
|
120 | <div class="buttons"> | |
121 | ${h.submit('save',_('Create Repository'),class_="btn")} |
|
121 | ${h.submit('save',_('Create Repository'),class_="btn")} | |
122 | </div> |
|
122 | </div> | |
123 | </div> |
|
123 | </div> | |
124 | </div> |
|
124 | </div> | |
125 | <script> |
|
125 | <script> | |
126 | $(document).ready(function(){ |
|
126 | $(document).ready(function(){ | |
127 | var setCopyPermsOption = function(group_val){ |
|
127 | var setCopyPermsOption = function(group_val){ | |
128 | if(group_val != "-1"){ |
|
128 | if(group_val != "-1"){ | |
129 | $('#copy_perms').show() |
|
129 | $('#copy_perms').show() | |
130 | } |
|
130 | } | |
131 | else{ |
|
131 | else{ | |
132 | $('#copy_perms').hide(); |
|
132 | $('#copy_perms').hide(); | |
133 | } |
|
133 | } | |
134 | }; |
|
134 | }; | |
135 |
|
135 | |||
136 | $('#remote_clone_toggle').on('click', function(e){ |
|
136 | $('#remote_clone_toggle').on('click', function(e){ | |
137 | $('#remote_clone').show(); |
|
137 | $('#remote_clone').show(); | |
138 | e.preventDefault(); |
|
138 | e.preventDefault(); | |
139 | }); |
|
139 | }); | |
140 |
|
140 | |||
141 | if($('#remote_clone input').hasClass('error')){ |
|
141 | if($('#remote_clone input').hasClass('error')){ | |
142 | $('#remote_clone').show(); |
|
142 | $('#remote_clone').show(); | |
143 | } |
|
143 | } | |
144 | if($('#remote_clone input').val()){ |
|
144 | if($('#remote_clone input').val()){ | |
145 | $('#remote_clone').show(); |
|
145 | $('#remote_clone').show(); | |
146 | } |
|
146 | } | |
147 |
|
147 | |||
148 | $("#repo_group").select2({ |
|
148 | $("#repo_group").select2({ | |
149 | 'containerCssClass': "drop-menu", |
|
149 | 'containerCssClass': "drop-menu", | |
150 | 'dropdownCssClass': "drop-menu-dropdown", |
|
150 | 'dropdownCssClass': "drop-menu-dropdown", | |
151 | 'dropdownAutoWidth': true, |
|
151 | 'dropdownAutoWidth': true, | |
152 | 'width': "resolve" |
|
152 | 'width': "resolve" | |
153 | }); |
|
153 | }); | |
154 |
|
154 | |||
155 | setCopyPermsOption($('#repo_group').val()); |
|
155 | setCopyPermsOption($('#repo_group').val()); | |
156 | $("#repo_group").on("change", function(e) { |
|
156 | $("#repo_group").on("change", function(e) { | |
157 | setCopyPermsOption(e.val) |
|
157 | setCopyPermsOption(e.val) | |
158 | }); |
|
158 | }); | |
159 |
|
159 | |||
160 | $('#repo_name').focus(); |
|
160 | $('#repo_name').focus(); | |
161 |
|
161 | |||
162 | $('#select_my_group').on('click', function(e){ |
|
162 | $('#select_my_group').on('click', function(e){ | |
163 | e.preventDefault(); |
|
163 | e.preventDefault(); | |
164 | $("#repo_group").val($(this).data('personalGroupId')).trigger("change"); |
|
164 | $("#repo_group").val($(this).data('personalGroupId')).trigger("change"); | |
165 | }) |
|
165 | }) | |
166 |
|
166 | |||
167 | }) |
|
167 | }) | |
168 | </script> |
|
168 | </script> | |
169 | ${h.end_form()} |
|
169 | ${h.end_form()} |
General Comments 0
You need to be logged in to leave comments.
Login now